요번엔 Vector의 Container를 다뤄보려고 한다.
문자 그대로 Container, 뭔가 Data를 담는 구조? 의 종류를 알아보는 것!
Vector
- Vector는 연속된 컨테이너로, dynamic-size array이고 Python의 List와 유사하다.
- Data들은 연속된 메모리에 저장된다.
- Random Access를 지원한다. ( Direct access by index )
- 끝으머리에 data Insertion / Deletion 지원한다.
- Element가 추가되면서 Resize는 자동으로 한다.
- Vector는 기본적으로 begin / end / size / capacity 정보를 갖고 있다.
Vector - Initialization
- <vector> header 파일이 필요하다.
- vector 안에 data에 대해선 type을 명확히 해줘야 한다. 그리고 python List와는 달리 vector는 오직 한 종류의 data를 가질 수 있다.
#include <iostream>
#include <vector>
int main(void)
{
//Direct vector initialization
std::vector<int> vec1 = {1,3,5,7,2};
//From an array
int arr[] = {9,10,11,8};
std::vector<int> vec2(std::begin(arr),std::end(arr));
//With a specific size and value
std::vector<int> vec3(5,10);
std::cout<<vec1.at(3)<<std::endl;
std::cout<<vec2.at(3)<<std::endl;
std::cout<<vec3.at(3)<<std::endl;
// 7
// 8
// 10
return 0;
}
- vec3(5,10) 의 경우 size 5를 10으로 채워야 해서 {10, 10, 10, 10, 10} 꼴로 vector가 만들어진다.
Vector - Insertion
- push_back / insert method를 이용하면 된다. 문자 그대로 push back! 뒤에서 Element하나씩 툭툭!
#include <iostream>
#include <vector>
int main(void)
{
using namespace std;
vector<int> vec1;
//add by push back
vec1.push_back(11);
vec1.push_back(22);
vec1.push_back(11);
//print
cout << vec1.at(0)<< vec1.at(1)<< vec1.at(2) << endl;
//112211
return 0;
}
Vector - Deletion
- pop_back / erase method로 element를 지울 수 있다.
#include <iostream>
#include <vector>
int main(void)
{
std::vector<int> vec1 = {1,2,3,4};
// 마지막 element를 뺍시다
vec1.pop_back();
std::vector<int> vec2 = {1,2,3,4};
// 3번 째 element를 빼라
vec2.erase(vec2.begin()+2);
return 0;
}
- vec2.begin()은 첫 번째 element에 iterator를 의미한다. iterator가 무엇이냐? 그건 아래에 계속..
Iteration - Range - Based for Loops
- C++11 에선 range-based for loops를 지원한다.
- for ( declaration : range ) statements; --> for ~ in ~ 요런 Python 구문과 유사하다.
- 중간에 auto 라는게 쓰이는데, 이것은 data type을 적절하게 알아서 매칭해주는 Key다. 유용!
#include <iostream>
#include <vector>
int main(void)
{
std::vector<int> num = {5,4,1};
for(int val : num)
{
std::cout << val << std::endl;
}
for(auto val : num)
{
std::cout << val << std::endl;
}
// 5
// 4
// 1
// 5
// 4
// 1
return 0;
}
- 이번엔 iterator를 이용해서 vector iteration을 돌려보자
#include <iostream>
#include <vector>
int main(void)
{
std::vector<int> vec = {3,1,2};
// using iterator
for(std::vector<int>::iterator iter = vec.begin();iter !=vec.end(); ++iter)
{
std::cout << *iter << std::endl;
}
// 3
// 1
// 2
return 0;
}
Iterator
- C++ iterator는 C에서 pointer와 비슷한 역할을 한다. --> container에 접근하고, 메모리를 돌아댕길 수 있게 해 줌
- Container마다 iterator type은 명확히 해서 선언해줘야 한다.
- Vector의 경우 begin() / end() method로 iterator의 포인팅을 지정해 줄 수 있는데,
--> begin의 경우 첫 번째 element 위치를 가르키고, end의 경우 마지막 + 1 번 째 element 위치를 가르킨다.
- 위 vector delete에서 봤던 erase 함수 활용의 경우, iterator를 사용한 case!
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vec = {1,2,3,4};
vec.erase(vec.begin()+1);
// index 1에 해당하는(여기선 값 2) 부분 제거
std::cout << vec.at(1);
//3
}
Finding Elements
- std::find function으로 element를 찾을 수 있다.
- find 함수는 찾으려는 element의 위치를 포인팅하고 있는 iterator를 반환한다.
- std::distance의 경우 element 위치의 offset을 반환해줘서 index를 계산할 수 있게 해준다.
int main()
{
std::vector<int> vec = {4,3,1,2};
// 2 라는 놈 위치를 찾아보자
std::vector<int>::iterator iter = std::find(vec.begin(),vec.end(),2);
if( iter != vec.end())
{
std::cout<<"find it:"<<std::distance(vec.begin(),iter) << std::endl; //DISTANCE!로 index계산이용!!
}
// find it:3
else
{
std::cout << "No data" <<std::endl;
}
return 0;
}
와나 .. Accumulate / list 에 대해서 쓰고 있었는데 글이 날라 갔다..
아 다시 쓰긴 구찮고, 코드 적어놨던 걸로 대체
ㅡㅡ 아 짜증나
int main(void)
{
std::vector<int> vec = {3,6,9,12};
// SUM all elements in the vector
int sum = std::accumulate(vec.begin(),vec.end(),0);
// 초기 seed 값 0이면 SUM 구할 때 0을 더함,
// 그 외 Calculation이 하고 싶으면 원하는 custom 함수를 만들고 4번 째 인자로 추가
std::cout << "sum : " << sum << std::endl;
// sum : 30
return 0;
}
- Accumulate는 container 안에 element들을 다 더해주는 함수인데, #include <numeric> header파일이 필요하다.
List
- list는 vector와 달리 non-contigious memory allocation을 지원하며 역시 #include <list>가 필요하다.
- Element 삽입/제거가 어느 포인트에서나 가능고 random access는 지원하지 않는다.(Python의 deque 구조란다)
int main()
{
// Default Initialization ( empty list )
std::list<std::string> list1;
// Direct Initialization
std::list<std::string> list2 =
{"DATA","SCIENTIST","YEAH"};
// Initialize with a specific number of elements with a specific value
std::list<std::string> list3(5,"Hi");
for(auto str : list1)
{
std::cout << str << std::endl;
}
return 0;
#include <string>
int main()
{
std::list<int> list1 = {5,3,1,2,4};
list1.push_back(6);
list1.push_front(0);
//0,5,3,1,2,4,6
auto it = list1.begin();
std::advance(it,4);
list1.insert(it,9);
//0,5,3,1,9,2,4,6
std::advance(it,-4);
list1.erase(it);
//5,3,1,9,2,4,6
list1.pop_back();
//5,3,1,9,2,4
list1.pop_front();
//3,1,9,2,4
return 0;
}
- list의 iteration은 forward / backward interaor를 사용한다.
#include <iostream>
#include <list>
int main()
{
using namespace std;
list<int> lst = {5,3,1,2,4};
//Forward iteration
for(auto it = lst.begin();it!=lst.end();++it)
{
cout << *it << " ";
}
cout << endl;
//5 3 1 2 4
// //Reverse iteration
for(auto rit = lst.rbegin();rit!=lst.rend();++rit)
{
cout << *rit << " ";
}
// //4 2 1 3 5
}
여기까지가 날라간 부분이고 ㅜㅜ 다시 정신차리고 공부 시작
list - Iteration
- list에서 end()는 마지막 element + 1 위치를 가르키고, rend()는 맨 처음 element - 1 위치를 가르킨다.
- 개념적으로 보면 맨 처음과 끝을 이어주는 Sentinel이 있다고 보면 되는데, 이는 C++ Implementation 시스템에 따라 다르므로 무조건 옳다고 말할 순 없다.
list - Memory
- list memory 주소 찍어보기
#include <iostream>
#include <set>
#include <list>
using namespace std;
int main()
{
std::list<int> lst = {3,2,5,1};
for(std::list<int>::iterator it = lst.begin();
it != lst.end();++it)
{
std::cout << &(*it) << " ";
}
std::cout << &(*lst.end()) << std::endl;
std::cout << &(*lst.begin());
// 0xfa6b50 0xfa6b70 0xfa6b90 0xfa6bb0 0x61fdd0
// 0xfa6b50
}
using namespace std;
int main()
{
std::list<int> lst = {3,2,5,1};
for(std::list<int>::reverse_iterator rit = lst.rbegin();
rit != lst.rend();++rit)
{
std::cout << &(*rit) << " ";
}
std::cout << &(*lst.rbegin())<<std::endl;;
std::cout << &(*lst.rend());
// 0x736bb0 0x736b90 0x736b70 0x736b50 0x736bb0
// 0x61fdd0
}
#include <iostream>
#include <set>
#include <list>
using namespace std;
int main()
{
std::list<int> lst = {3,2,5,1};
for(std::list<int>::iterator it = lst.begin();
it != lst.end();++it)
{
std::cout << &(*it) << " ";
}
std::cout << &(*lst.end()) << std::endl;
std::cout << &(*lst.begin()) << std::endl;
for(std::list<int>::reverse_iterator rit = lst.rbegin();
rit != lst.rend();++rit)
{
std::cout << &(*rit) << " ";
}
std::cout << &(*lst.rbegin())<<std::endl;;
std::cout << &(*lst.rend());
// 0xf76b50 0xf76b70 0xf76b90 0xf76bb0 0x61fdb0
// 0xf76b50
// 0xf76bb0 0xf76b90 0xf76b70 0xf76b50 0xf76bb0
// 0x61fdb0
}
- forward iterator에서 lst.end()의 주소 값과, backward에서 lst.rend()의 주소값이 같은 것으로 보아 list는 sentinel로 순환구조를 가질 수 있다는 추정가능 ( C++ 작업환경에 따라 달라질 수 있는 내용이므로 확실시는 X )
map
- Map은 key - value 꼴로 element들을 저장하는 container다.
- Map에서 key는 unique value이고 sort / identify 할 때 사용되며 value는 중복된 값을 가져도 된다.
- Default로, map에 있는 element들은 key의 오름차순으로 정렬된다.(hash table과 다른점)
map의 구조
- Node는 std::pair object이고 key 와 value를 각각 first , second attribute으로 갖는다.
- Binary Search / Balanced Tree 이다. --> Time complexity를 계산 시 O(log(n))의 구조를 갖는다.
map - Initialization
- #include <map> header 필요
- Key와 value의 Data type이 명확히 선언되어야 한다.
#include <iostream>
#include <map>
#include <string>
int main()
{
// Initialization an empty map
std::map<std::string , int> map1;
// Using an initializer list
std::map<std::string, int> map2 =
{
{"ACE",19},{"MARCUS",30},{"BINNY",30}
};
return 0;
}
map - Insertion
- Key / Value 쌍을 insert function 또는 [ ] 를 통해서 추가한다.
#include <iostream>
#include <map>
#include <string>
int main()
{
using namespace std;
map<string,int> ageMap;
// 첫 번 째 방법
ageMap.insert({"ACE",30});
// 두 번 째 방법
ageMap["MARCUS"] = 30l;
// 세 번 째 방법
ageMap.insert(make_pair("ROLE",40));
// 만약!
ageMap.insert({"ACE",29}); //<-- 이미 key가 있어서 무시
ageMap["ACE"] = 29; // 이렇게 하면 Data Update 가능
cout << ageMap["ACE"]<<endl;
cout << ageMap["MARCUS"]<<endl;
cout << ageMap["ROLE"]<<endl;
cout << ageMap["ACE2222"]<<endl;
// 29
// 30
// 40
// 0
return 0;
}
- 없는 key에 대해선 아무 값 반환 ( 여기선 0 )
map - Search
- find라는 finction으로 key 값을 찾으며 이는 key 값을 포인팅하는 iterator를 반환한다.
#include <iostream>
#include <map>
#include <string>
int main()
{
using namespace std;
map<string , int>agemap={
{"A",1},
{"B",2},
{"C",3}
};
string name = "B";
if(agemap.find(name) != agemap.end())
{
cout << agemap[name]<<endl;
} // agemap이라는 것이 있는가 check, 이걸 아래와 같이 효율화가능
//2
auto it = agemap.find("B");
if(it != agemap.end())
{
cout << it->first << ":" << it->second <<endl;
}
// B:2
return 0;
}
- find가 element를 못 찾으면 agemap.find(name) --> agemap.end()로 포인팅 한다.
if(agemap.find(name) != agemap.end())
{
cout << agemap.find(name)->first<<":"<<agemap[name]<<endl;
}
//C:3
//요런식으로도 표현 가능
- find가 iterator를 return 한다는 것을잊지 말자!!
map - Deletion
#include <iostream>
#include <map>
#include <string>
int main()
{
std::map<std::string,int> map1 =
{
{"A",100},
{"B",2}
};
std::cout << map1["A"] << std::endl;
// 100
map1.erase("A");
std::cout << map1["A"] << std::endl;
std::cout << map1["RANDOM"] << std::endl;
// 0
// 0
map1.erase("C"); // No Error, 항상 KEY값이 존재하는 지 체크
return 0;
}
- map은 없는 KEY 값에 대해선 Value를 Read 하려고 할 때, 없는 Value라고 Error를 띄우지 않고 0을 반환하네
map - Iteration
- range-based for 문 이나 iterator로 map 값을 반환하며, KEY의 오름차순 순서로 진행한다.
#include <iostream>
#include <map>
#include <string>
int main()
{
std::map<std::string,int> map1 =
{
{"A",11},
{"B",22},
{"C",33}
};
// Range - Based for loop [ Pair란 놈 등장;;;;]
for(std::pair<std::string,int> pair : map1)
{
std::cout << pair.first << ":" << pair.second << std::endl;
}
// A:11
// B:22
// C:33
// Iterator
for(std::map<std::string,int>::iterator it=map1.begin();it!=map1.end();++it)
{
std::cout<<it->first<<":"<<it->second<<std::endl;
}
// A:11
// B:22
// C:33
return 0;
}
이제 Last!!!
set
- set은 unique한 element들을 저장하는 container이다.
- element들의 key = value이다.
- 기본적으로 sorting이 된 구조로 저장되어 있다.
- set은 balanced binary search tree 구조이다.
- #include <set> header 파일 필요
#include <iostream>
#include <set>
int main(void)
{
//initialization
std::set<int> set1;
//initialize with list
std::set<int> set2 = {3,2,4,1,5};
return 0;
}
set의 Insertion / Search / Deletion
- Insert는 insert 함수를 이용하고
- Delete는 erase 함수를 쓰거나 iterator를 사용한다.
//initialize with list
std::set<int> set2 = {3,2,4,1,5};
//Add
set2.insert(6);
//Remove
set2.erase(1); // by value
auto it = set2.find(2);
if(it != set2.end())
{
set2.erase(it);
} // by iterator
return 0;
set - Iteration
- map 때와 비슷하게 range-based for문이나 iterator를 통해 접근한다.
std::set<int> set2 = {3,2,4,1,5};
//Add
set2.insert(6);
//Remove
set2.erase(1); // by value
auto it = set2.find(2);
if(it != set2.end())
{
set2.erase(it);
} // by iterator
// Iterate using a range-based for loop
for(auto elem : set2)
{
std::cout << elem << " ";
}//3 4 5 6
std::cout << std::endl;
//Iterator로 접근
for(auto it = set2.begin();it!=set2.end();++it)
{
std::cout << *it << " ";
}//3 4 5 6
- SET는 중복을 허용하지 않기 때문에, 극단적으로 아래와 같이 선언해도 결과값은 중복제거!! + 자동 SORTING 오름차순
#include <iostream>
#include <set>
int main()
{
std::set<int> set2 = {3,3,1,3,3,3,3,3,2,4,5,7,7,7};
//Add
set2.insert(6);
set2.insert(6);
set2.insert(6);
set2.insert(6);
set2.insert(6);
set2.insert(6);
//Remove
set2.erase(1); // by value
for(auto elem : set2)
{
std::cout << elem << " ";
}
std::cout << std::endl;
//Iterator로 접근
for(auto it = set2.begin();it!=set2.end();++it)
{
std::cout << *it << " ";
}
// 2 3 4 5 6 7
// 2 3 4 5 6 7
}
- auto만 잘 쓰면 쉽게 쉽게 접근 가능하다
- auto 아니면 요렇게 복잡하게 접근 ㅠㅠ
- 자세히 다룬 Container 외 에도 이렇게 많은 종류의 Container가 있다.
이번엔 요기까지..!
'SW 만학도 > C++' 카테고리의 다른 글
6. Out_of_class Definition & Operator Overloading (0) | 2024.04.10 |
---|---|
5. Classes (0) | 2024.04.09 |
4. Functions and Memory Management (1) | 2024.03.29 |
2. C++ Standard Library (2) (1) | 2024.03.22 |
1. C++ Standard Library (1) (0) | 2024.03.21 |