c++ STL의 컨테이너 / Algorithm STL

한정화·2023년 4월 8일
0

#230408 토 ~ 230415 토

c++에서 제공하는 표준 템플릿 라이브러리 STL에는 vector, stack, queue, pair, map 등의 컨테이너가 있다. 컨테이너의 종류와 각 멤버함수들을 알아보자.

  1. 컨테이너와 iterator란?
  2. vector 컨테이너
  3. stack 컨테이너
  4. queue 컨테이너
  5. pair 컨테이너
  6. Map 컨테이너
  7. Algorithm STL
  8. 범위 기반 for문
  9. 람다식

1. 컨테이너와 iterator란?

컨테이너는 같은 타입의 데이터, 객체를 저장하고 관리하는 역할을 한다.
iterator(반복자)는 컨테이너 원소의 주소를 저장하는 포인터이다.

(컨테이너 이름)<자료형>::iterator (반복자 이름); 

2. vector 컨테이너

1) vector란?

크기가 자유로운 배열을 구현한 컨테이너이다.

pop_back()
front()
back()
begin()
end()
size()
empty()
insert(it, data)
erase(it)

2) front(), back(), begin(), end(), push_back()

#include <iostream>
#include <vector>   //헤더파일
using namespace std;

int main(){
    vector<int> v; //벡터 생성
    
    for(int i=0;i<5;i++) v.push_back(i+1); // push_back() 마지막에 data 추가

    cout<<"값 변경 전: ";
    cout<<v.front()<<' ';  //front() 맨 앞 원소 반환
    cout<<v.back()<<'\n';  //back() 맨 뒤 원소 반환 
    
    vector<int>::iterator it;  //컨테이너 원소를 가리키는 포인터 iterator 선언
    it = v.begin();        //begin() 맨 앞 원소의 주소를 반환
    //iterator가 첫 번째 원소를 가리키도록 
    
    while(it!=v.end()){   //end() 맨 뒤 원소의 주소 + 1의 주소를 반환
        *it+=10;          // **it : iterator가 가리키는 값을 10 증가시킴
        it++;             // it 주소 한 칸 증가
    }
    
    cout<<"값 변경 후: ";
    cout<<v.front()<<' ';
    cout<<v.back()<<'\n';
}

3) size(), empty(), insert, erase

#include <iostream>
#include <vector>
#include <string>
using namespace std;

int main(){
    vector<string> memStack;  //후입선출 멤버 list
    vector<string>::iterator it;
    
    while(1){
        cout<<"=================Member=================\n";
        cout<<"추가(1)  삭제(2)  멤버수 출력(3)  종료(4)\n";
        int f; cin>>f;
        
        if(f==1){
            cout<<"추가할 멤버의 이름 : ";
            string stmp; cin>>stmp;
            it = memStack.begin();
            memStack.insert(it, stmp);  //insert(it, data) it가 가리키는 위치에 data 추가
        }
        
        else if (f==2){
            cout<<"삭제할 멤버의 이름 : ";
            string targetS; cin>>targetS;
            it = memStack.begin();
            while(it!=memStack.end()){
                if(targetS == *it) break;
            }
            memStack.erase(it);       //erase(it) it가 가리키는 위치의 원소 삭제
        }
        
        else if(f==3){
            if(memStack.empty()){
                cout<<"리스트에 아무도 없습니다.\n";  //empty() 벡터가 비어있으면 true 반환
            }
            else cout<< memStack.size()<<'\n';        //size() 벡터의 사이즈 반환
        }
        
        else{
            break;
        }
    }
    cout<<"프로그램이 종료되었습니다.";
    return 0; 
}

3. stack 컨테이너

1) stack이란?

LIFO 형식의 크기가 자유로운 배열이다.

pop()
top()
empty()
size()

2) 멤버함수

#include <iostream>
#include <stack>
using namespace std;

int main(){
    stack<string> bookStack;     //바닥에 쌓아올리는 나의 책더미들~
    while(1){
        cout<<"==================Book==================\n";
        cout<<"쌓기(1) 내리기(2) 책 개수 출력(3) 종료(4)\n";
        int f; cin>>f;
        
        if(f==1){
            cout<<"올려둘 책 제목 : ";
            string stmp; cin>>stmp;
            bookStack.push(stmp);   //push(data) 맨 뒤에 data 추가
        }
        
        else if(f==2){
            bookStack.pop();        //pop()  스택의 마지막 원소 제거
        }
        
        else if(f==3){
            if(bookStack.empty()){  //empty() 스택이 비어있으면 true
                cout<<"바닥에 책이 없는데용?\n";
            } 
            else cout<<bookStack.size();  //size()  스택 사이즈 반환
        }
        
        else if(f==4){
            break;
        }
    }
    cout<<"엄마에게 혼나서 더 이상 바닥에 책을 둘 수 없어.. 책꽂이에 꽂도록 하자..\n";
    return 0;
}

3. queue 컨테이너

1) stack vs queue
FIFO 형식의 크기가 자유로운 배열

pop()
top()
empty()
size()

stack은 LIFO 형식이므로 pop()이 가장 마지막 원소를 제거하는 반면, queue에서는 맨 앞 원소를 제거한다. 반면, push()는 stack과 queue 라이브러리 모두에서 맨 뒤에 원소를 추가한다.


4. pair 컨테이너

1) pair란?

두 개체(자료형이 다를 수도 있다)를 단일 개체로 묶어 관리할 수 있게 해주는 컨테이너이다.

pair<자료형1, 자료형2> 이름(사이즈);
pair<int, string> p;
p.first = 3;
p.second = "hi";

2) 헤더 파일

#include <utility>

참고로 vector 헤더파일은 utility 헤더를 포함하고 있기 때문에 vector와 pair 컨테이너를 함께 쓰고 싶다면 vector 헤더파일만 선언해주면 된다.

3) 활용

#include <iostream>
#include <utility>   //헤더 파일
#include <string>
using namespace std;

int main(){
    pair<string, int> myInfo;  //pair 컨테이너 선언
    
    myInfo.first = "한정화";   //.first  첫 번째 원소 (여기에선 string 개체)
    myInfo.second = 23 ;       //.second 두 번째 원소 (여기에선 int 개체)
    
    cout<<"내 이름은 "<<myInfo.first<<"이고, 나이는 "<<myInfo.second<<"입니다.";
}

#include <iostream>
#include <vector>   //utility 헤더 포함
#include <string>
using namespace std;

int main(){
    vector<pair<string, int>> wishTem(3);
    
    for(int i=0;i<3;i++){
        string stmp; 
        int ptmp;
        
        cout<<"위시템 상품명 : ";
        cin>>stmp;
        cout<<"위시템 가격 : ";
        cin>>ptmp;
            
        wishTem[i].first = stmp;    //벡터의 i번째 pair의 값 설정
        wishTem[i].second = ptmp;
    }
    
    for(int i=0;i<3;i++){
        cout<<"위시템 "<<wishTem[i].first<<", "<<wishTem[i].second<<"원\n";
    }
}

5. Map 컨테이너

1) map이란?

key와 value를 묶어 관리하는 컨테이너이다.

  • 선언 :
map<key자료형, value자료형> 이름(사이즈);
  • map 변수 생성 방법 :
#include <iostream>
#include <map>
#include <string>
using namespace std;

int main(){
    map<int, string> student;
    
    student.insert({30,"junghwa"});    //insert({key, value}) map 변수 생성방법2
    student[20] = "soyeon";            //이름[key] = value  map 변수 생성방법1
    
    cout<<student.size()<<'\n';        //size() 사이즈 반환
    
    cout<<"id가 30인 학생은"<<student[30]<<"입니다.";
}

2) 멤버함수

begin()
end()
size()
empty()
find(key)
erase(it)
operator[key]()

3) 활용

#include <iostream>
#include <map>
using namespace std;

int main(){
    map<string, string> EngKodict;
    
    cout<<"=======dictionary======\n";
    while(1){
        string word, meaning;
        cout<<"영단어와 뜻 입력>> ";
        cin>>word>>meaning;
        
        if(word=="end" && meaning=="end") break;
        
        EngKodict.insert({word, meaning});
    }
    
    cout<<"===English to korean===\n";
    while(1){
        string word;
        cout<<"영단어 : ";
        cin>>word;
        if(word=="end") break; 
        cout<<word<<"의 뜻 : "<<EngKodict[word]<<'\n';
    }
}


6. Algorithm STL

algorithm STL 은 다양한 컨테이너와 iterator를 이용해 다양한 기능을 제공한다.

(1) sort

quick sort로 구현되어있어 속도가 매우 빠르다.
cpp sort(정렬 시작 주소, 정렬 끝 주소+1, 비교함수)

  • 배열 오름차순 정렬
#include <iostream>
#include <algorithm>
using namespace std;
int arr[5];

int main(){
    cout<<"배열 데이터 입력: ";
    for(int i=0;i<5;i++){
        cin>>arr[i];
    }
    sort(arr, arr+5);  //sort(정렬 시작 주소, 정렬 끝 주소+1)
    //c++에서 배열은 첫 번째 원소를 가리키는 주소이므로 이렇게 쓸 수 있다. 
    
    cout<<"오름차순 정렬: ";
    for(int i=0;i<5;i++) cout<<arr[i]<<' ';
}

  • 배열 내림차순 정렬 (비교함수를 매개변수로 전달)
#include <iostream>
#include <algorithm>
using namespace std;
int arr[5];

bool cmp(int a, int b){   //비교함수
    if(a > b) return true;  //자리 변경 o 
    else return false;      //자리 변경 x
}

int main(){
    cout<<"배열 데이터 입력: ";
    for(int i=0;i<5;i++){
        cin>>arr[i];
    }
    sort(arr, arr+5, cmp);  //sort(정렬 시작 주소, 정렬 끝 주소+1, 비교함수)
    
    cout<<"내림차순 정렬: ";
    for(int i=0;i<5;i++) cout<<arr[i]<<' ';
}

  • vector 정렬
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
    cout<<"배열 사이즈 입력: ";
    int n;
    cin>>n;
    cout<<"배열 데이터 입력: ";
    vector<string> v(n);
    for(int i=0;i<n;i++){
        cin>>v[i];
    }
    sort(v.begin(), v.end());  //sort(정렬 시작 주소, 정렬 끝 주소+1)
    cout<<"사전순 정렬: ";
    for(int i=0;i<n;i++) cout<<v[i]<<' ';
}

  • vector 사전역순 정렬
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

bool cmp(string s1, string s2){
    if(s1 < s2) return true;
    else return false;
}

int main(){
    int n;
    cout<<"배열 사이즈 입력: ";
    cin>>n;
    vector <string> v(n);
    cout<<"배열 데이터 입력: ";
    for(int i=0;i<n;i++){
        cin>>v[i];
    }
    sort(v.begin(), v.end());
    
    cout<<"사전역순 정렬: ";
    for(int i=0;i<n;i++) cout<<v[i]<<' ';
}

  • 구조체 정렬
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct person {
    int age;
    string name;
};

bool cmp(person p1, person p2) {
    if (p1.age == p2.age) {
        return p1.name < p2.name;
    }
    return p1.age < p2.age;
}

int main() {
    int n;
    cout << "배열 사이즈 입력: ";
    cin >> n;
    vector<person> v(n);
    cout << "-" << n << "명의 데이터 입력-\n";
    for (int i = 0; i < n; i++) cin >> v[i].age >> v[i].name;

    sort(v.begin(), v.end(), cmp);
    cout << "-나이순 정렬-\n";
    for (int i = 0; i < n; i++) cout << v[i].age << ' ' << v[i].name << '\n';
}

(2) for_each 함수

for_each(시작 주소, 끝 주소+1, 반복실행함수)

  • 예제
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

void print(int n) {
	cout << n << ' ';
}

int main() {
	vector <int> v = { 1,2,3,4,5 };
	for_each(v.begin(), v.end(), print); // for_each(시작주소, 끝주소+1, 반복실행함수)
}

위 코드에서 for_each 함수의 작동 원리는 다음과 동일하다 :

	vector <int>::iterator it;
	for (it= v.begin(); it < v.end(); it++) {
		print(*it);
}

8. 기타 함수

(1) 범위 기반 for문

c++11에 새로 추가됐다고 한다(오예)
for(자료형 변수명 : 컨테이너 이름)

  • 예제 1 (정수를 원소로 가지고 있는 vector 컨테이너)
#include <iostream>
#include <vector>
using namespace std;

int main() {
	vector <int> v = { 1,2,3,4,5 };
	for (int k : v) {  // for(자료형 변수명 : 컨테이너 이름)  
		cout << k << ' ';
	}
}
  • 예제 2 (클래스를 원소로 가지고 있는 vector 컨테이너)
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Person {
public:
	Person(string name, int age) : name(name), age(age) {}
	void newYear() { age++; }
	void getInfo() { cout << name << "은 " << age << "살이다. \n"; }

private:
	int age;
	string name;
};

vector <Person> v;
//vector <Person> v;  ->빈 컨테이너라서 오류 안 남
//vector <Person> v(3); -> 3개를 생성해두는 것이므로 기본 생성자가 없으면 에러가 남

int main() {
	Person junghwa("junghwa", 23), soyeon("soyeon", 23), heesu("heesu", 23);
	v.push_back(junghwa);
	v.push_back(soyeon);
	v.push_back(heesu);

	for (Person p : v) {  // for(자료형 변수명 : 컨테이너 이름)  
		p.getInfo();
	}

	cout << "Happy New Year!\n";

	for (Person p : v) {  // for(자료형 변수명 : 컨테이너 이름)  
		p.newYear();
		p.getInfo();
	}

}

(2) auto

auto는 컴파일러가 자료형을 추측하도록 지시하는 명령어이다. 이 또한 c++11에 추가된 기능이다.

#include <iostream>
#include <string>
using namespace std;

int main() {
	auto p1 = 3.14;
	auto n = 3;
	auto str = "파이 소숫점 이하 20자리까지 외울 수 있는 사람?";
	cout << p1 << '\n' << n << '\n' << str;
}

  • 배열은 불가능하다.

  • 클래스, 구조체도 가능하다.
    위의 (1)범위 기반 for문 예제 2에서의

for (Person p : v) {  // for(자료형 변수명 : 컨테이너 이름)  
		p.getInfo();
	}

이 코드를

for(auto c: v){
	c.getInfo();
}

로도 쓸 수 있다.

위와 같이 범위 기반 for문과 함께 쓸 때 auto가 유용하게 쓰인다.


9. 람다식

0개의 댓글