๊ธฐ๋ณธ ํน์ง
์๊ฐ ๋ณต์ก๋
| ์ฐ์ฐ | ์๊ฐ ๋ณต์ก๋ |
|---|---|
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;
๊ธฐ๋ณธ ํน์ง
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 ๋ชจ๋ ์ญ์
๊ธฐ๋ณธ ํน์ง
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
๊ธฐ๋ณธ ํน์ง
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 (์ ๋ ฌ๋จ)
}
๊ธฐ๋ณธ ํน์ง
์๊ฐ ๋ณต์ก๋
| ์ฐ์ฐ | ํ๊ท | ์ต์ |
|---|---|---|
| Insertion | O(1) | O(n) |
| Deletion | O(1) | O(n) |
| Search | O(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_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_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 | unordered_set |
|---|---|---|
| ๊ตฌํ | Red-Black Tree | Hash 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 ๋น๊ต
| ํน์ฑ | map | unordered_map |
|---|---|---|
| ๊ตฌํ | Red-Black Tree | Hash Table |
| ํค ์ ๋ ฌ | โ | โ |
| ํ๊ท ๊ฒ์ | O(log n) | O(1) โ |
| ์ต์ ๊ฒ์ | O(log n) โ | O(n) |
| ์ค๋ฌด ์ฌ์ฉ ๋น๋ | ์ค๊ฐ | โ ๋งค์ฐ ๋์ |
์ข์ ํด์ ํจ์์ ์กฐ๊ฑด
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
์ฑ๋ฅ ์ต์ ํ ํ
unordered_* ์ฌ์ฉ ์:
reserve() ์ฌ์ฉmax_load_factor ์กฐ์ ์ผ๋ก ์ถฉ๋ ์ ์ดset/map ์ฌ์ฉ ์:
lower_bound, upper_bound ํ์ฉ์ผ๋ฐ์ ์ธ ๊ถ์ฅ์ฌํญ:
unordered_map (์ค๋ฌด ํ์ค)mapmultiset / 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