[C++] Set/Map

choohaยท2026๋…„ 1์›” 30์ผ

C++

๋ชฉ๋ก ๋ณด๊ธฐ
23/23

๐Ÿ“š C++ Set/Map

๐Ÿ”ธ set (์ง‘ํ•ฉ)

๊ธฐ๋ณธ ํŠน์ง•

  • Red-Black Tree (๋ ˆ๋“œ-๋ธ”๋ž™ ํŠธ๋ฆฌ)๋กœ ๊ตฌํ˜„
  • ์ค‘๋ณต ๋ถˆ๊ฐ€ - ๊ฐ ์š”์†Œ๋Š” ์œ ์ผํ•ด์•ผ ํ•จ
  • ์ž๋™ ์ •๋ ฌ - ์‚ฝ์ž… ์‹œ ์ž๋™์œผ๋กœ ์ •๋ ฌ๋œ ์ƒํƒœ ์œ ์ง€
  • Balanced BST ํŠน์„ฑ์ƒ ๋ชจ๋“  ์—ฐ์‚ฐ์ด O(log n) ๋ณด์žฅ

์‹œ๊ฐ„ ๋ณต์žก๋„

์—ฐ์‚ฐ์‹œ๊ฐ„ ๋ณต์žก๋„
Insertion (insert)O(log n)
Deletion (erase)O(log n)
Search (find)O(log n)
Access (์ตœ์†Œ/์ตœ๋Œ€)O(log n)

๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ (Red-Black Tree)

๋ ˆ๋“œ-๋ธ”๋ž™ ํŠธ๋ฆฌ ์†์„ฑ:
1. ๋ชจ๋“  ๋…ธ๋“œ๋Š” Red ๋˜๋Š” Black
2. ๋ฃจํŠธ๋Š” ํ•ญ์ƒ Black
3. ๋ชจ๋“  ๋ฆฌํ”„(NULL)๋Š” Black
4. Red ๋…ธ๋“œ์˜ ์ž์‹์€ ๋ชจ๋‘ Black
5. ๋ชจ๋“  ๊ฒฝ๋กœ์˜ Black ๋…ธ๋“œ ๊ฐœ์ˆ˜๋Š” ๋™์ผ

์˜ˆ์‹œ: {10, 20, 30, 40, 50}
         30(B)
        /     \
     20(B)    40(B)
      /         \
   10(R)       50(R)

Stack:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ root (pointer)  โ”‚ โ†’ ๋ฃจํŠธ ๋…ธ๋“œ
โ”‚ size            โ”‚ โ†’ ์š”์†Œ ๊ฐœ์ˆ˜
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Heap (๊ฐ ๋…ธ๋“œ):
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ color (R/B)       โ”‚
โ”‚ data              โ”‚
โ”‚ left (pointer)    โ”‚
โ”‚ right (pointer)   โ”‚
โ”‚ parent (pointer)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

์‚ฌ์šฉ ์˜ˆ์‹œ

#include <set>
#include <iostream>

std::set<int> s;

// โœ… ์‚ฝ์ž… - O(log n)
s.insert(30);
s.insert(10);
s.insert(20);
s.insert(10);  // ์ค‘๋ณต - ์‚ฝ์ž… ์•ˆ ๋จ!

// โœ… ์ž๋™ ์ •๋ ฌ๋จ
for (int val : s) {
    std::cout << val << " ";  // 10 20 30
}

// โœ… ๊ฒ€์ƒ‰ - O(log n)
auto it = s.find(20);
if (it != s.end()) {
    std::cout << "Found: " << *it;
}

// โœ… ์‚ญ์ œ - O(log n)
s.erase(20);

// โœ… ๋ฒ”์œ„ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰
auto lower = s.lower_bound(15);  // >= 15์ธ ์ฒซ ์š”์†Œ
auto upper = s.upper_bound(25);  // > 25์ธ ์ฒซ ์š”์†Œ

์ปค์Šคํ…€ ๋น„๊ต ํ•จ์ˆ˜

// ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ
std::set<int, std::greater<int>> descSet;

// ์ปค์Šคํ…€ ๋น„๊ต์ž
struct Person {
    std::string name;
    int age;
};

struct CompareByAge {
    bool operator()(const Person& a, const Person& b) const {
        return a.age < b.age;
    }
};

std::set<Person, CompareByAge> people;

๐Ÿ”ธ multiset (์ค‘๋ณต ํ—ˆ์šฉ ์ง‘ํ•ฉ)

๊ธฐ๋ณธ ํŠน์ง•

  • set๊ณผ ๋™์ผํ•˜๊ฒŒ Red-Black Tree๋กœ ๊ตฌํ˜„
  • ์ค‘๋ณต ํ—ˆ์šฉ - ๋™์ผํ•œ ๊ฐ’์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฝ์ž… ๊ฐ€๋Šฅ
  • ์ž๋™ ์ •๋ ฌ ์œ ์ง€
  • ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” set๊ณผ ๋™์ผ

set vs multiset

std::set<int> s;
s.insert(10);
s.insert(10);
s.insert(10);
std::cout << s.size();  // 1 (์ค‘๋ณต ๋ฌด์‹œ)

std::multiset<int> ms;
ms.insert(10);
ms.insert(10);
ms.insert(10);
std::cout << ms.size();  // 3 (์ค‘๋ณต ํ—ˆ์šฉ)

์ค‘๋ณต ์š”์†Œ ์ฒ˜๋ฆฌ

std::multiset<int> ms = {10, 20, 20, 30, 30, 30};

// โœ… ํŠน์ • ๊ฐ’์˜ ๊ฐœ์ˆ˜ ์„ธ๊ธฐ - O(log n + k), k๋Š” ์ค‘๋ณต ๊ฐœ์ˆ˜
int count = ms.count(30);  // 3

// โœ… ํŠน์ • ๊ฐ’์˜ ๋ฒ”์œ„ ์ฐพ๊ธฐ
auto range = ms.equal_range(30);
for (auto it = range.first; it != range.second; ++it) {
    std::cout << *it << " ";  // 30 30 30
}

// โœ… ํŠน์ • ๊ฐ’ ํ•˜๋‚˜๋งŒ ์‚ญ์ œ
auto it = ms.find(30);
if (it != ms.end()) {
    ms.erase(it);  // 30 ํ•˜๋‚˜๋งŒ ์‚ญ์ œ
}

// โœ… ํŠน์ • ๊ฐ’ ๋ชจ๋‘ ์‚ญ์ œ
ms.erase(30);  // 30 ๋ชจ๋‘ ์‚ญ์ œ

๐Ÿ”ธ map (ํ‚ค-๊ฐ’ ์Œ)

๊ธฐ๋ณธ ํŠน์ง•

  • Red-Black Tree๋กœ ๊ตฌํ˜„
  • ํ‚ค(Key)๋Š” ์ค‘๋ณต ๋ถˆ๊ฐ€, ๊ฐ’(Value)์€ ์ค‘๋ณต ๊ฐ€๋Šฅ
  • ํ‚ค ๊ธฐ์ค€์œผ๋กœ ์ž๋™ ์ •๋ ฌ
  • set๊ณผ ๋™์ผํ•œ ์‹œ๊ฐ„ ๋ณต์žก๋„

์‹œ๊ฐ„ ๋ณต์žก๋„

์—ฐ์‚ฐ์‹œ๊ฐ„ ๋ณต์žก๋„
Insertion (insert, operator[])O(log n)
Deletion (erase)O(log n)
Search (find, operator[])O(log n)

๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ

Heap (๊ฐ ๋…ธ๋“œ):
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ color (R/B)       โ”‚
โ”‚ pair<Key, Value>  โ”‚ โ† key์™€ value๋ฅผ ํ•จ๊ป˜ ์ €์žฅ
โ”‚ left (pointer)    โ”‚
โ”‚ right (pointer)   โ”‚
โ”‚ parent (pointer)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

์‚ฌ์šฉ ์˜ˆ์‹œ

#include <map>
#include <string>

std::map<std::string, int> ages;

// โœ… ์‚ฝ์ž… ๋ฐฉ๋ฒ• 1: operator[] - O(log n)
ages["Alice"] = 25;
ages["Bob"] = 30;

// โœ… ์‚ฝ์ž… ๋ฐฉ๋ฒ• 2: insert - O(log n)
ages.insert({"Charlie", 35});
ages.insert(std::make_pair("David", 40));

// โœ… ๊ฒ€์ƒ‰ - O(log n)
auto it = ages.find("Alice");
if (it != ages.end()) {
    std::cout << it->first << ": " << it->second;  // Alice: 25
}

// โš ๏ธ operator[]์˜ ์ฃผ์˜์ 
int age = ages["Eve"];  // ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ์ž๋™ ์ƒ์„ฑ! (value๋Š” 0์œผ๋กœ ์ดˆ๊ธฐํ™”)
std::cout << ages.size();  // 5 (Eve๊ฐ€ ์ถ”๊ฐ€๋จ)

// โœ… ์•ˆ์ „ํ•œ ๊ฒ€์ƒ‰: at() ์‚ฌ์šฉ
try {
    int age = ages.at("Frank");  // ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ
} catch (const std::out_of_range& e) {
    std::cout << "Key not found!";
}

iterator ํ™œ์šฉ

std::map<std::string, int> scores = {
    {"Alice", 90},
    {"Bob", 85},
    {"Charlie", 95}
};

// โœ… ์ˆœํšŒ (ํ‚ค ๊ธฐ์ค€ ์ •๋ ฌ๋œ ์ˆœ์„œ)
for (const auto& [name, score] : scores) {
    std::cout << name << ": " << score << "\n";
}
// Alice: 90
// Bob: 85
// Charlie: 95

๐Ÿ”ธ multimap (์ค‘๋ณต ํ‚ค ํ—ˆ์šฉ)

๊ธฐ๋ณธ ํŠน์ง•

  • map๊ณผ ๋™์ผํ•˜๊ฒŒ Red-Black Tree๋กœ ๊ตฌํ˜„
  • ํ‚ค ์ค‘๋ณต ํ—ˆ์šฉ
  • operator[] ์‚ฌ์šฉ ๋ถˆ๊ฐ€ (์–ด๋–ค ๊ฐ’์„ ๋ฐ˜ํ™˜ํ• ์ง€ ๋ชจํ˜ธ)

์‚ฌ์šฉ ์˜ˆ์‹œ

std::multimap<std::string, int> scores;

scores.insert({"Alice", 90});
scores.insert({"Alice", 85});  // ๊ฐ™์€ ํ‚ค์— ๋‹ค๋ฅธ ๊ฐ’
scores.insert({"Bob", 95});

// โœ… ํŠน์ • ํ‚ค์˜ ๋ชจ๋“  ๊ฐ’ ์ฐพ๊ธฐ
auto range = scores.equal_range("Alice");
for (auto it = range.first; it != range.second; ++it) {
    std::cout << it->second << " ";  // 85 90 (์ •๋ ฌ๋จ)
}

๐Ÿ”ธ unordered_set (ํ•ด์‹œ ์ง‘ํ•ฉ)

๊ธฐ๋ณธ ํŠน์ง•

  • Hash Table๋กœ ๊ตฌํ˜„
  • ์ค‘๋ณต ๋ถˆ๊ฐ€
  • ์ •๋ ฌ๋˜์ง€ ์•Š์Œ (์ˆœ์„œ ๋ณด์žฅ ์•ˆ ๋จ)
  • ํ‰๊ท  O(1), ์ตœ์•… O(n) ์‹œ๊ฐ„ ๋ณต์žก๋„

์‹œ๊ฐ„ ๋ณต์žก๋„

์—ฐ์‚ฐํ‰๊ท ์ตœ์•…
InsertionO(1)O(n)
DeletionO(1)O(n)
SearchO(1)O(n)

๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ (Hash Table with Separate Chaining)

Hash Table ๊ตฌ์กฐ:

ํ•ด์‹œ ํ•จ์ˆ˜: hash(value) % bucket_count

Bucket Array (vector):
โ”Œโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”
โ”‚  0 โ”‚  1  โ”‚  2  โ”‚  3  โ”‚  4  โ”‚
โ””โ”€โ”€โ”ฌโ”€โ”€โ”ดโ”€โ”€โ”ฌโ”€โ”€โ”ดโ”€โ”€โ”ฌโ”€โ”€โ”ดโ”€โ”€โ”ฌโ”€โ”€โ”ดโ”€โ”€โ”ฌโ”€โ”€โ”˜
   โ”‚     โ”‚     โ”‚     โ”‚     โ”‚
   โ†“     โ†“     โ†“     โ†“     โ†“
  [10]  [21]  NULL [13]  [24]
   โ†“     โ†“           โ†“     โ†“
  [30]  NULL        [33]  NULL

๊ฐ ๋ฒ„ํ‚ท์€ Linked List๋กœ ๊ตฌํ˜„ (Separate Chaining)
ํ•ด์‹œ ์ถฉ๋Œ ๋ฐœ์ƒ ์‹œ ๊ฐ™์€ ๋ฒ„ํ‚ท์— ์—ฐ๊ฒฐ

Stack:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ buckets (array) โ”‚ โ†’ ๋ฒ„ํ‚ท ๋ฐฐ์—ด
โ”‚ size            โ”‚ โ†’ ์š”์†Œ ๊ฐœ์ˆ˜
โ”‚ bucket_count    โ”‚ โ†’ ๋ฒ„ํ‚ท ๊ฐœ์ˆ˜
โ”‚ max_load_factor โ”‚ โ†’ ์ตœ๋Œ€ ๋กœ๋“œ ํŒฉํ„ฐ (๊ธฐ๋ณธ 1.0)
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ํ•ด์‹œ ํ•จ์ˆ˜์™€ ์ถฉ๋Œ ์ฒ˜๋ฆฌ

// ํ•ด์‹œ ๊ฐ’ ๊ณ„์‚ฐ ์˜ˆ์‹œ
hash<int> hashFunc;
size_t hashValue = hashFunc(42);

// ๋ฒ„ํ‚ท ์ธ๋ฑ์Šค = hash(value) % bucket_count
size_t bucketIndex = hashValue % bucket_count;

// โœ… Separate Chaining
// ๊ฐ™์€ ๋ฒ„ํ‚ท์— ์—ฌ๋Ÿฌ ์š”์†Œ๊ฐ€ linked list๋กœ ์—ฐ๊ฒฐ๋จ

Load Factor์™€ Rehashing

std::unordered_set<int> us;

// Load Factor = size / bucket_count
// ๊ธฐ๋ณธ max_load_factor = 1.0

us.insert(10);
us.insert(20);
us.insert(30);

std::cout << "Size: " << us.size() << "\n";
std::cout << "Bucket count: " << us.bucket_count() << "\n";
std::cout << "Load factor: " << us.load_factor() << "\n";
std::cout << "Max load factor: " << us.max_load_factor() << "\n";

// โš ๏ธ load_factor > max_load_factor ๋˜๋ฉด Rehashing ๋ฐœ์ƒ!
// Rehashing: O(n) - ๋ชจ๋“  ์š”์†Œ๋ฅผ ์ƒˆ ๋ฒ„ํ‚ท์— ์žฌ๋ฐฐ์น˜

Rehashing ์ตœ์ ํ™”

std::unordered_set<int> us;

// โœ… ๋ฏธ๋ฆฌ ๋ฒ„ํ‚ท ์˜ˆ์•ฝ - rehashing ๋ฐฉ์ง€
us.reserve(1000);  // ์ตœ์†Œ 1000๊ฐœ ์š”์†Œ๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ๋„๋ก ๋ฒ„ํ‚ท ํ™•๋ณด

// โœ… max_load_factor ์กฐ์ •
us.max_load_factor(0.5);  // ๋กœ๋“œ ํŒฉํ„ฐ๋ฅผ ๋‚ฎ์ถฐ์„œ ์ถฉ๋Œ ๊ฐ์†Œ (๋ฉ”๋ชจ๋ฆฌ trade-off)

์‚ฌ์šฉ ์˜ˆ์‹œ

#include <unordered_set>

std::unordered_set<int> us;

// โœ… ์‚ฝ์ž… - ํ‰๊ท  O(1)
us.insert(10);
us.insert(20);
us.insert(30);

// โœ… ๊ฒ€์ƒ‰ - ํ‰๊ท  O(1)
if (us.find(20) != us.end()) {
    std::cout << "Found!";
}

// โš ๏ธ ์ˆœ์„œ ๋ณด์žฅ ์•ˆ ๋จ!
for (int val : us) {
    std::cout << val << " ";  // ์ˆœ์„œ ์˜ˆ์ธก ๋ถˆ๊ฐ€
}

์ปค์Šคํ…€ ํƒ€์ž…์„ ์œ„ํ•œ Hash Function ์ •์˜

๋ฐฉ๋ฒ• 1: Hash Function Object ์ƒ์„ฑ

struct Person {
    std::string name;
    int age;
    
    bool operator==(const Person& other) const {
        return name == other.name && age == other.age;
    }
};

// ์ปค์Šคํ…€ ํ•ด์‹œ ํ•จ์ˆ˜ ๊ฐ์ฒด
struct PersonHash {
    size_t operator()(const Person& p) const {
        // ์—ฌ๋Ÿฌ ํ•„๋“œ๋ฅผ ์กฐํ•ฉํ•œ ํ•ด์‹œ ๊ฐ’ ๊ณ„์‚ฐ
        size_t h1 = std::hash<std::string>{}(p.name);
        size_t h2 = std::hash<int>{}(p.age);
        return h1 ^ (h2 << 1);  // XOR๊ณผ shift ์กฐํ•ฉ
    }
};

// ์‚ฌ์šฉ
std::unordered_set<Person, PersonHash> people;
people.insert({"Alice", 25});

๋ฐฉ๋ฒ• 2: std ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ํŠน์ˆ˜ํ™” ์ฃผ์ž…

struct Person {
    std::string name;
    int age;
    
    bool operator==(const Person& other) const {
        return name == other.name && age == other.age;
    }
};

// std::hash ํŠน์ˆ˜ํ™”
namespace std {
    template<>
    struct hash<Person> {
        size_t operator()(const Person& p) const {
            size_t h1 = hash<string>{}(p.name);
            size_t h2 = hash<int>{}(p.age);
            return h1 ^ (h2 << 1);
        }
    };
}

// ์‚ฌ์šฉ (์ถ”๊ฐ€ ํ…œํ”Œ๋ฆฟ ์ธ์ž ๋ถˆํ•„์š”)
std::unordered_set<Person> people;
people.insert({"Alice", 25});

์ปค์Šคํ…€ Equality Operator

// ๋ฐฉ๋ฒ• 1: ํด๋ž˜์Šค ๋‚ด๋ถ€์— operator== ์ •์˜ (์œ„ ์˜ˆ์‹œ ์ฐธ๊ณ )

// ๋ฐฉ๋ฒ• 2: ๋ณ„๋„์˜ ํ•จ์ˆ˜ ๊ฐ์ฒด
struct PersonEqual {
    bool operator()(const Person& a, const Person& b) const {
        return a.name == b.name && a.age == b.age;
    }
};

std::unordered_set<Person, PersonHash, PersonEqual> people;

๐Ÿ”ธ unordered_map (ํ•ด์‹œ ๋งต)

๊ธฐ๋ณธ ํŠน์ง•

  • Hash Table๋กœ ๊ตฌํ˜„
  • ํ‚ค ์ค‘๋ณต ๋ถˆ๊ฐ€
  • unordered_set๊ณผ ๋™์ผํ•œ ์‹œ๊ฐ„ ๋ณต์žก๋„
  • ์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋จ
  • ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฉด์ ‘์—์„œ ์ž์ฃผ ์ถœ์ œ๋จ

์‹œ๊ฐ„ ๋ณต์žก๋„

์—ฐ์‚ฐํ‰๊ท ์ตœ์•…
Insertion (insert, operator[])O(1)O(n)
Deletion (erase)O(1)O(n)
Search (find, operator[])O(1)O(n)

๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ

Bucket Array:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  0   โ”‚  1  โ”‚  2   โ”‚  3  โ”‚
โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”ดโ”€โ”€โ”ฌโ”€โ”€โ”€โ”ดโ”€โ”€โ”ฌโ”€โ”€โ”€โ”ดโ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜
   โ”‚     โ”‚      โ”‚      โ”‚
   โ†“     โ†“      โ†“      โ†“
 {k1:v1} NULL {k2:v2} NULL
   โ†“                   โ†“
 {k3:v3}             {k4:v4}

๊ฐ ๋…ธ๋“œ๋Š” pair<Key, Value> ์ €์žฅ

์‚ฌ์šฉ ์˜ˆ์‹œ

#include <unordered_map>
#include <string>

std::unordered_map<std::string, int> phoneBook;

// โœ… ์‚ฝ์ž… - ํ‰๊ท  O(1)
phoneBook["Alice"] = 1234;
phoneBook["Bob"] = 5678;
phoneBook.insert({"Charlie", 9012});

// โœ… ๊ฒ€์ƒ‰ - ํ‰๊ท  O(1)
auto it = phoneBook.find("Alice");
if (it != phoneBook.end()) {
    std::cout << it->first << ": " << it->second;
}

// โœ… operator[] - ํ‰๊ท  O(1)
int number = phoneBook["Alice"];  // 1234

// โš ๏ธ ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ์ž๋™ ์ƒ์„ฑ
int unknown = phoneBook["David"];  // David ํ‚ค ์ƒ์„ฑ, value = 0

// โœ… ์„ฑ๋Šฅ ์ตœ์ ํ™”
phoneBook.reserve(10000);  // ์˜ˆ์ƒ ํฌ๊ธฐ ๋ฏธ๋ฆฌ ์˜ˆ์•ฝ

์‹ค๋ฌด ํ™œ์šฉ ์˜ˆ์‹œ

// ๋นˆ๋„์ˆ˜ ์นด์šดํŒ…
std::unordered_map<char, int> freq;
std::string text = "hello world";
for (char c : text) {
    freq[c]++;
}

// ์บ์‹ฑ (Memoization)
std::unordered_map<int, int> cache;
int fibonacci(int n) {
    if (n <= 1) return n;
    if (cache.find(n) != cache.end()) {
        return cache[n];
    }
    cache[n] = fibonacci(n-1) + fibonacci(n-2);
    return cache[n];
}

// ๊ทธ๋ž˜ํ”„ ์ธ์ ‘ ๋ฆฌ์ŠคํŠธ
std::unordered_map<int, std::vector<int>> graph;
graph[1] = {2, 3};
graph[2] = {4};

์ปค์Šคํ…€ ํƒ€์ž… ์˜ˆ์‹œ

struct Point {
    int x, y;
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }
};

struct PointHash {
    size_t operator()(const Point& p) const {
        return std::hash<int>{}(p.x) ^ (std::hash<int>{}(p.y) << 1);
    }
};

std::unordered_map<Point, std::string, PointHash> locations;
locations[{0, 0}] = "Origin";
locations[{10, 20}] = "Point A";

๐Ÿ”ธ unordered_multiset / unordered_multimap

๊ธฐ๋ณธ ํŠน์ง•

  • ๊ฐ๊ฐ unordered_set, unordered_map์˜ ์ค‘๋ณต ํ—ˆ์šฉ ๋ฒ„์ „
  • ์‹œ๊ฐ„ ๋ณต์žก๋„ ๋™์ผ
  • unordered_multimap์€ operator[] ์‚ฌ์šฉ ๋ถˆ๊ฐ€

์‚ฌ์šฉ ์˜ˆ์‹œ

std::unordered_multiset<int> ums = {10, 20, 20, 30, 30, 30};

// ํŠน์ • ๊ฐ’์˜ ๊ฐœ์ˆ˜
int count = ums.count(30);  // 3

// ํŠน์ • ๊ฐ’ ๋ชจ๋‘ ์ฐพ๊ธฐ
auto range = ums.equal_range(30);
for (auto it = range.first; it != range.second; ++it) {
    std::cout << *it << " ";
}

๐Ÿ”ธ set vs unordered_set ๋น„๊ต

์„ฑ๋Šฅ ๋น„๊ต

ํŠน์„ฑsetunordered_set
๊ตฌํ˜„Red-Black TreeHash Table
์ •๋ ฌโœ… ์ž๋™ ์ •๋ ฌโŒ ์ •๋ ฌ ์•ˆ ๋จ
ํ‰๊ท  ๊ฒ€์ƒ‰O(log n)O(1) โœ…
์ตœ์•… ๊ฒ€์ƒ‰O(log n) โœ…O(n)
๋ฉ”๋ชจ๋ฆฌ์ ์Œ๋งŽ์Œ (๋ฒ„ํ‚ท ๋ฐฐ์—ด)
์ˆœ์„œ ๋ณด์žฅโœ…โŒ
iterator ์•ˆ์ •์„ฑ๋†’์Œ๋‚ฎ์Œ (rehashing ์‹œ ๋ฌดํšจํ™”)

์„ ํƒ ๊ฐ€์ด๋“œ

// โœ… set์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ:
// 1. ์ •๋ ฌ๋œ ์ˆœ์„œ๊ฐ€ ํ•„์š”ํ•  ๋•Œ
std::set<int> sorted = {30, 10, 20};
for (int val : sorted) {
    std::cout << val << " ";  // 10 20 30 (์ •๋ ฌ๋จ)
}

// 2. ๋ฒ”์œ„ ๊ฒ€์ƒ‰์ด ํ•„์š”ํ•  ๋•Œ
auto lower = sorted.lower_bound(15);
auto upper = sorted.upper_bound(25);

// 3. ์ตœ์•…์˜ ๊ฒฝ์šฐ์—๋„ ์ผ์ •ํ•œ ์„ฑ๋Šฅ์ด ํ•„์š”ํ•  ๋•Œ (O(log n) ๋ณด์žฅ)

// โœ… unordered_set์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ:
// 1. ๋น ๋ฅธ ๊ฒ€์ƒ‰์ด ์ตœ์šฐ์„ ์ผ ๋•Œ (ํ‰๊ท  O(1))
std::unordered_set<int> fast;

// 2. ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•˜์ง€ ์•Š์„ ๋•Œ

// 3. ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๋ฅผ ๋ฏธ๋ฆฌ ์•Œ๊ณ  reserve ๊ฐ€๋Šฅํ•  ๋•Œ
fast.reserve(10000);

map vs unordered_map ๋น„๊ต

ํŠน์„ฑmapunordered_map
๊ตฌํ˜„Red-Black TreeHash Table
ํ‚ค ์ •๋ ฌโœ…โŒ
ํ‰๊ท  ๊ฒ€์ƒ‰O(log n)O(1) โœ…
์ตœ์•… ๊ฒ€์ƒ‰O(log n) โœ…O(n)
์‹ค๋ฌด ์‚ฌ์šฉ ๋นˆ๋„์ค‘๊ฐ„โœ… ๋งค์šฐ ๋†’์Œ

๐Ÿ”ธ Hash Function ์„ค๊ณ„ ์›์น™

์ข‹์€ ํ•ด์‹œ ํ•จ์ˆ˜์˜ ์กฐ๊ฑด
1. ๊ฒฐ์ •๋ก ์  (Deterministic): ๊ฐ™์€ ์ž…๋ ฅ์€ ํ•ญ์ƒ ๊ฐ™์€ ํ•ด์‹œ ๊ฐ’
2. ๊ท ๋“ฑ ๋ถ„ํฌ (Uniform Distribution): ํ•ด์‹œ ๊ฐ’์ด ๊ณ ๋ฅด๊ฒŒ ๋ถ„ํฌ
3. ํšจ์œจ์„ฑ (Efficient): ๊ณ„์‚ฐ์ด ๋น ๋ฆ„ (O(1))
4. ์ถฉ๋Œ ์ตœ์†Œํ™”: ์„œ๋กœ ๋‹ค๋ฅธ ์ž…๋ ฅ์— ๋Œ€ํ•ด ๋‹ค๋ฅธ ํ•ด์‹œ ๊ฐ’

ํ•ด์‹œ ์กฐํ•ฉ ๊ธฐ๋ฒ•

// โŒ ๋‚˜์œ ์˜ˆ: ๋‹จ์ˆœ ๋”ํ•˜๊ธฐ (์ถฉ๋Œ ๋งŽ์Œ)
size_t badHash = hash<int>{}(x) + hash<int>{}(y);

// โœ… ์ข‹์€ ์˜ˆ 1: XOR + Shift
size_t goodHash1 = hash<int>{}(x) ^ (hash<int>{}(y) << 1);

// โœ… ์ข‹์€ ์˜ˆ 2: Boost ์Šคํƒ€์ผ
size_t goodHash2 = hash<int>{}(x);
goodHash2 ^= hash<int>{}(y) + 0x9e3779b9 + (goodHash2 << 6) + (goodHash2 >> 2);

// โœ… ์ข‹์€ ์˜ˆ 3: ์—ฌ๋Ÿฌ ํ•„๋“œ ์กฐํ•ฉ
struct Data {
    int a, b, c;
};

size_t hash_value(const Data& d) {
    size_t seed = 0;
    seed ^= hash<int>{}(d.a) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    seed ^= hash<int>{}(d.b) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    seed ^= hash<int>{}(d.c) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    return seed;
}

๐Ÿ”ธ ํ•ต์‹ฌ ์ •๋ฆฌ

์ปจํ…Œ์ด๋„ˆ ์„ ํƒ ๊ฐ€์ด๋“œ

์ˆœ์„œ๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?
โ”œโ”€ YES โ†’ set / map
โ”‚         - ์ •๋ ฌ๋œ ์ˆœ์„œ ๋ณด์žฅ
โ”‚         - ๋ฒ”์œ„ ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅ
โ”‚         - O(log n) ์„ฑ๋Šฅ ๋ณด์žฅ
โ”‚
โ””โ”€ NO โ†’ unordered_set / unordered_map
          - ํ‰๊ท  O(1) ์„ฑ๋Šฅ
          - ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๋†’์Œ
          - rehashing ์ฃผ์˜

์ค‘๋ณต์ด ํ•„์š”ํ•œ๊ฐ€?
โ”œโ”€ YES โ†’ multiset / multimap / unordered_multiset / unordered_multimap
โ””โ”€ NO โ†’ set / map / unordered_set / unordered_map

ํ‚ค-๊ฐ’ ์Œ์ด ํ•„์š”ํ•œ๊ฐ€?
โ”œโ”€ YES โ†’ map / unordered_map
โ””โ”€ NO โ†’ set / unordered_set

์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

  1. unordered_* ์‚ฌ์šฉ ์‹œ:

    • ์˜ˆ์ƒ ํฌ๊ธฐ๋ฅผ ์•„๋Š” ๊ฒฝ์šฐ reserve() ์‚ฌ์šฉ
    • max_load_factor ์กฐ์ •์œผ๋กœ ์ถฉ๋Œ ์ œ์–ด
    • ์ข‹์€ ํ•ด์‹œ ํ•จ์ˆ˜ ์„ค๊ณ„
  2. set/map ์‚ฌ์šฉ ์‹œ:

    • ์ปค์Šคํ…€ ๋น„๊ต ํ•จ์ˆ˜๋กœ ์ •๋ ฌ ์ˆœ์„œ ์ œ์–ด
    • lower_bound, upper_bound ํ™œ์šฉ
  3. ์ผ๋ฐ˜์ ์ธ ๊ถŒ์žฅ์‚ฌํ•ญ:

    • ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ: unordered_map (์‹ค๋ฌด ํ‘œ์ค€)
    • ์ •๋ ฌ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ: map
    • ์ค‘๋ณต์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ: multiset / multimap

์‹ค๋ฌด์—์„œ ์ž์ฃผ ์“ฐ์ด๋Š” ํŒจํ„ด

// 1. ๋นˆ๋„์ˆ˜ ์นด์šดํŒ…
unordered_map<string, int> wordCount;

// 2. ์ค‘๋ณต ์ œ๊ฑฐ
unordered_set<int> uniqueValues;

// 3. ์บ์‹ฑ/๋ฉ”๋ชจ์ด์ œ์ด์…˜
unordered_map<int, Result> cache;

// 4. ๊ทธ๋ž˜ํ”„ ํ‘œํ˜„
unordered_map<Node, vector<Node>> adjacencyList;

// 5. ๋น ๋ฅธ ์กฐํšŒ ํ…Œ์ด๋ธ”
unordered_map<string, UserData> userDatabase;

<์ฐธ๊ณ  ์ž๋ฃŒ>

์ฝ”๋“œ์—†๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ
std::set
std::map
std::unordered_set
std::unordered_map
Red-Black Tree
Hash Table

0๊ฐœ์˜ ๋Œ“๊ธ€