씹어먹는 C++ 14일차

❗참고 : https://modoocode.com/224

C++ 표준 템플릿 라이브러리 (Standard Template Library, STL)

  • 표준 STL은 std namespace에 존재함
  • STL = container + iterator + algorithm
  • 아래의 세 가지 라이브러리를 의미
    • 컨테이너(container) : 임의 타입의 객체를 보관
    • 반복자(iterator) : 컨테이너에 보관된 원소에 접근
    • 알고리즘(algorithm) : 반복자들을 가지고 일련의 작업을 수행

Sequence container

  • Like array
  • 객체를 순차적으로 보관
  • vector, list, deque

Associative container

  • key를 이용하여 value를 찾아줌
  • key-value 구조를 가짐
  • template library 이므로 key와 value가 임의 type의 객체가 될 수 있음

set / multiset

  • 특정 key가 연관 컨테이너에 존재하는지 유무
  • 원소를 insert 시 자동 정렬됨
  • set : 중복된 원소가 존재하지 않음
  • multiset : 중복된 원소가 존재할 수 있음

set

  • 원소를 추가/삭제하는 작업의 시간 복잡도 : O(logN)
  • 원소를 찾는(find) 작업의 시간 복잡도 : O(logN)
    • vector의 find는 O(N)
#include <iostream>
#include <set>

template <typename T>
void print_set(std::set<T>& s) {
  // print all date in set
  std::cout << "[" ;
  // for (typename std::set<T>::iterator it = s.begin() ; it != s.end(); ++it) {
  for (auto it = s.begin() ; it != s.end(); ++it) {
    std::cout << *it << " ";
  }
  std::cout << " ] " << std::endl;
}

int main() {
  std::set<int> s;
  s.insert(10);
  s.insert(20);
  s.insert(50);
  s.insert(30);

  std::cout << "순서대로 정렬됨 " << std::endl;
  print_set(s);

  int findNum = 20;
  auto it = s.find(findNum);
  if (it != s.end()) {
    std::cout << findNum << " is in set" << std::endl;
  } else {
    std::cout << findNum << " isn't in set" << std::endl;
  }

  return 0;
}

multiset

  • 중복된 key값을 저장
  • lower_bound : http://www.cplusplus.com/reference/algorithm/lower_bound/
  • upper_bound : http://www.cplusplus.com/reference/algorithm/upper_bound/
#include <iostream>
#include <set>

using namespace std;
int main() {
  multiset<int> ms;

  ms.insert(10);
  ms.insert(10);
  ms.insert(20);
  ms.insert(20);

  multiset<int>::iterator it;
  for (it = ms.begin() ; it != ms.end(); it++) {
    cout << *it << " ";
  }
  cout << endl;

  cout << "ms.count(10) : " << ms.count(10) << endl;
  // 10이 몇 개 들어있는지 출력됨

  cout << "10이 처음 나온 시작 : " << ms.lower_bound(10) << endl;
  cout << "10의 마지막 위치 : " << ms.upper_bound(10) << endl;


  return 0;
}

map / multimap

  • 특정 key가 존재한다면 이에 대응되는 값이 무엇인지 질의
    • set보다 사용하는 메모리가 큼
    • key의 존재 유무만 궁금하다면 set 사용을 추천
  • map : 중복된 원소가 존재하지 않음
  • multimap : 중복된 원소가 존재할 수 있음

map

  • key와 key에 대응되는 value를 같이 보관함
  • 원소를 추가/삭제하는 작업의 시간 복잡도 : O(logN)
  • 원소를 찾는(find) 작업의 시간 복잡도 : O(logN)
#include <iostream>
#include <map>
#include <string>

template <typename K, typename V>
void print_map(std::map<K, V>& m) {
  // 맵의 모든 원소들을 출력하기
  for (auto it = m.begin(); it != m.end(); ++it) {
    std::cout << it->first << " " << it->second << std::endl;
  }
}

int main() {
  std::map<std::string, double> mlist;  // 2022 공휴일
  
  mlist.insert(std::pair<std::string, double>("신정", 1.1));  // 시작부터 토요일..
  mlist.insert(std::pair<std::string, double>("설날", 2.1));
  mlist.insert(std::pair<std::string, double>("31절", 3.1));
  mlist.insert(std::pair<std::string, double>("근로자의날", 5.1));  // 일ㅠ
  mlist.insert(std::pair<std::string, double>("어린이날", 5.5));
  mlist.insert(std::pair<std::string, double>("부처님오신날", 5.8));  // 일ㅠㅠ

  // 타입을 지정하지 않아도 간단히 std::make_pair 함수로 객체 생성 가능
  mlist.insert(std::make_pair("현충일", 6.6));
  mlist.insert(std::make_pair("광복절", 8.15));
  mlist.insert(std::make_pair("추석", 9.10));  // 토...!ㅠㅠ

  // 혹은 insert 를 안쓰더라도 [] 로 바로 원소를 추가
  mlist["개천절"] = 10.3;
  mlist["한글날"] = 10.9;  // 일ㅠㅠㅠㅠ
  mlist["크리스마스"] = 12.25;  // 일...잃은 공휴일 6일.....ㅂㅇ...

  print_map(mlist);

  std::cout << "key의 value는? " << mlist["설날"] << std::endl;
}

multimap

  • 중복된 key 값을 저장 가능
  • multimap의 경우, [] 연산자를 제공하지 않음
  • 중복된 key에 해당되는 value를 모두 확인할 때 : equal_range(“key”) 를 사용
#include <iostream>
#include <map>
#include <string>

using namespace std;

int main(void) {
  multimap<int, string> mm;
  mm.insert(make_pair(1, "c"));
  mm.insert(make_pair(1, "c++"));
  mm.insert(make_pair(1, "c#"));
  mm.insert(pair<int, string>(2, "java"));
  mm.insert(pair<int, string>(3, "python"));

  multimap<int, string>::iterator it;

  for (it = mm.begin() ; it != mm.end() ; it++) {
    cout << it->first << ", " << it->second << endl;
  }

  // print duplicate key (1)
  for (it = mm.equal_range(1).first ; it != mm.equal_range(1).second ; it++) {
    cout << it->first << ", " << it->second << endl;
  }


  return 0;
}

unordered_set / unordered_map

  • C++ 11에서 추가됨
  • 원소들이 정렬되어있지 않음
  • insert/erase/find 가 O(1) ~ O(N)으로 수행됨
    • insert/find에 해시 함수를 사용함
    • Hash collision이 발생하냐에 따라 평균 O(1), 최약의 경우 O(N)으로 수행됨
  • 최적화가 되어 있는 데이터라면, O(1)로 수행되는 unordered_set/map을 사용, 아니라면 O(logN)인 set/map을 사용

※ Big-O 시간 복잡도

O(1) : 상수 O(log N) O(N) : 선형 O(N logN) O(N^2_) : Square

#include <unordered__set>
#include <unordered_map>
  • https://en.cppreference.com/w/cpp/container/unordered_set
  • https://en.cppreference.com/w/cpp/container/unordered_map