2025.3.15
1. Search
- 원하는 데이터의 실제 저장 정보를 찾아나가는 방법
- 효율적인 검색을 위해 요구사항에 따라 적절한 자료구조로 데이터를 관리함.
| Search algorithm 종류 | ||
| Linear Search | Direct Addressing | Binary Search (ordered sequence) = 정렬되어있을시 유용 Hash (hash table) = 범위 벗어나거나 클 때 유용 trie (tree) |
| (sequence) , O(n) | (direct table), O(1) | |
| 가장 기본적인 순차탐색. 데이터의 수, 검색횟수가 적은 경우 사용 |
Key 값을 배열의 index로 사용 key값의 범위가 배열을 잡을 수 있는 수준일때 |
|
![]() |
너무 커지면 사용X![]() |
|
Linear Search의 경우 하나를 통채로 관리할 경우 n번 돌아야하지만 특정 기준으로 분류를 할 경우 효율적이 됨.
ex) 알파벳 첫 글자를 기준으로 해서 s칸에 son, l칸에 lee 이런 식으로 구분하면 N번 탐색할걸 N/26 번 돌면 됨
+ Direct Addressing의 경우 배열의 크기가 너무 커지면 사용이 어려운데 이를 보완하는 것이 필요함.
=> Hash!
Hash의 분류가 1:1로 매치되는 것이 Direct Addressing 임!
2. Hash
- 검색의 한가지 방법
- 현업에서도 많이 쓰이는 자료구조 알고리즘
- key 값과 일치하는 데이터를 효율적으로 검색하기 위해 사용
- key 값은 고유값 (ex) id, 주민번호)
- 인덱싱(indexing) 기법 활용
- 파이썬의 경우 dictinary, set을 기반으로 메모리를 늘려서 opener 방식을 사용함
![]() |
probing - 구한 bucket에 저장된 모든 원소들을 검색하며 원하는 data를 찾기 - 동일한 key값은 항상 같은 bucket - 다른 Key값도 같은 bucket가능 (하지만 충돌 발생) - 충돌을 최소화해야 성능이 잘나옴 * hash function 최대한 고유하게 * size를 크게 |
|
| 1. Hash Key | data의 고유값을 나타낼 수 있는 정보로 설정 | |
| 2. Hash Function | hash value 생성 : unsigned int / unsigned long long 일반적으로 key 값을 전부 활용하여 진법 변환 (직접 설정도 가능, key값의 일부만 사용하기도 함) |
|
| 3. Bucket | 나눗셈 법 : hash value % size | |
| 4. Collision 처리 방식 | chaining - bucket 별로 리스트화 하여 관리 | |
| 5. Hash Table Size | chaining시 - hash table에 등록되는 data수 이상의 2의 제곱수 or 소수 Unordered_set, map에서는 2의 제곱수로 알아서 설정됨 |
|
3. Hash - EXAMPLE
EXAMPLE 1

- EXAMPLE 2

- EXAMPLE 3

- 어떻게?
- hash function을 최대한 고유하게 만들기
- bucket을 최대한 많이 만들기
- EXAMPLE 4

이런식으로!
4. Hash Function
- Hash value를 고유하게 생성하는 방법 (반드시 고유할 필요는 없으나 고유하면 좋음)
- 일반적으로는 hash value를 최대한 고유하게 생성하여 충돌 확률을 낮춘다
- 만약, hash value의 범위가 unsigned long long범위를 벗어나면 %연산이 들어가므로 고유하지 않음.
- 개수가 고정되어 있을 때 수의 범위를 0부터로 맞춰주고 (max값 +1) 진법변환
- -100 ~ 100 범위의 1개 정수
- 0 ~ 100 범위의 4개 정수
- -100 ~ 100 범위의 3 정수
- 소문자 10자리
- 개수가 고정되지 않을 때, 수의 범위를 1부터로 맞춰주고 (max값 +1) 진법변환
- 소문자 4~10자리
- 대문자 4~10자리
- Hash Function 구현
- Hornor's method 활용 (K진법 -> 10진법)
| 5k^3 + 3k^2 + 2k^1 + 4k^0 = [{(0*k+5)*k+3}*k+2]*k+4 ① 0*k+5 ② ①*k+3 ③ ②*k +2 ④ ③*k +4 |
초기값 hash =0 ① hash = hash*k +5 ② hash = hash*k +3 ③ hash = hash*k +2 ④ hash = hash*k +4 |
key값 : 0~k-1 범위의 정수 n개 ull hash Func(int key[]) { ull hash = 0; for (int i = 0; i < n; i++) hash = hash * k + key[i]; return hash; } |
ex) 0 ~ 50 범위의 x, y => x*51 +y // 0 ~ 20 범위의 x, y , z => x*21^2 + y*21 + z
- Hash Tavle size
- hash value를 최대한 고유하게 설정했을 때 hash table size가 성능에 미치는 영향

5. Hash 구현방식
| inked list 직접 구현 chaining | ||
| 일반 배열로 open addressing | T htab[SIZE]; | |
| vector로 chaining | vector<T>htab[SIZE]; | n/size = 조절 |
| unordered_map, unordered_set |
unordered_map<T,U>um; unordered_set<T>us |
n/size = 1 * 특징은 set, map과 동일하나 순서 무관 |
6. Unordered Associative

- Hash table
- chaining (linked list)
- Forward Iterator (next만 사용 가능)
| Space | Search | Insert | Delete | |
| average | O(1) | O(1) | O(1) | O(1) |
| worst | O(n) | O(n) | O(n) | O(n) |
- key에 따라 bucket에 잘 분배되도록 hash function을 만들어야함
- 순서 중요X 넣을수록 뒤죽박죽. 순서가 필요하면 unordered_set,map말고 다른거 사용하는게 나음.

7. Predefined Functor
- Comparision operator
- less <T> : class
- less <T>() : function
- less <T>{} : function
// equal_to
template<class T>
struct equal_to {
bool operator()(const T& l, const T& r) const {
return l == r;
}
}
// less
template<class T>
struct less {
bool operator()(const T& l, const T& r) const {
return l < r;
}
}
// greater
template<class T>
struct greater {
bool operator()(const T& l, const T& r) const {
return l > r;
}
}
- Hash
| Requirement | Specialization 되어있는 type |
|
|
// ***key 값을 받아 hash value를 반환함.
struct hash {
size_t operator()(const Key& key) const{
return (hash value);
}
};
- Custom : Hash, KeyEqual -1
- hash : functor
- KeyEqual : equal_to operator (==) overloading
struct Key {
int x,y;
bool operator==(const Key& r) const {
return x == r.x && y == r.y;
}
};
struct CustomHash{
size_t operator()(const Key& key) const{
return key.x * 10000 + key.y;
}
};
unordered_set <Key, CustomHash> us;
unordered_map <Key, int, CustomHash> um;
- Custom : Hash, KeyEqual -2
- hash : functor
- KeyEqual : functor
struct Key { int x,y;};
struct MyHash{
size_t operator()(const Key& key) const{
return key.x * 10000 + key.y;
}
};
struct MyEqual{
bool operator()(const Key& r) const {
return x == r.x && y == r.y;
}
};
unordered_set <Key, MyHash, MyEqual> us;
unordered_map <Key, int, MyHash, MyEqual> um;
8. Policy
| rehash | reserve | load_factor | mas_load_factor |
| bucket_count를 늘려서 hash table를 제구성 O(n) |
bucket_count설정 설정한 값 이상의 2k크기로 설정됨 |
average number of elements per bucket n / bucket_count |
regash가 일어나는 threshold defalt 1.0 |
- htab.reserve(n)을 통하여 미리 hash table크기를 결정하면 불필요한 rehasing이 일어나지 않아 효율적일 것 같지만
- 실제 test해보면 별 차이 없고 오히려 느린 경우도 존재 = 굳이 reserve 안하고 써도 됨.
9. Unordered_set
template<
class Key,
class Hash = std::hash<key>,
class KeyEqual = std::equal_to<Key>
class Allocator = std::allocator<Key>
> class unordered_set;
// * size_t: unsigned long long
unordered_set<T> htab
unordered_set<T, Hash> htab
unordered_set<T, Hash, KeyEqual> htab
iteratorhtab.begin(), htab.end() // iteraor 반환 O(1)
bool htab.empty() // 비었으면 true 아님 false O(1)
size_t htab.size() // size 반환 O(1)
void htab.clear() // 초기화 O(n)
size_t htab.count(T key) // x개수 반환 0 or 1 O(1)
iterator htab.find(T key) // x의 iterator반환 없으면 htab.end()반환 O(1)
/* 검색 과정이 상수 시간대라 O(1)로 표현하지만
배열처럼 direct 접근이 아니라 경우에 따라 상수 값이 상당히 커질 수 있음. */
void htab.reserve(int n) // n이상의2^k로bucket_count설정
size_t htab.bucket(T key) // key 값의 bucket 번호 반환
size_t htab.bucket_count() // hash table bucket 개수
size_t htab.bucket_size(intbucket) // bucket의 element 개수
// insert O(1) - 구간은 구간만큼 - 등록된 값 있으면 무시
pair <iterator, bool> htab.insert(T key) // { 등록된iterator, 성공여부}
void htab.insert(iterator first, iterator last)
// erase O(1) - 구간은 구간만큼
iterator htab.erase(iterator pos)
iterator htab.erase(iterator first, iterator last) // 마지막으로지운element의다음iterator
size_t htab.erase(T key) // 성공여부 0,1
EXAMPLE
unordered_set<int> s;
for (int i = 5; i > 0; i--) s.insert(i);
for (auto x : s) cout << x << ' '; // 1 2 3 4 5 (순서 다를 수 있음)
for (auto it = s.begin(); it != s.end(); ++it) cout << *it << ' '; // 1 2 3 4 5
s.empty(); // 0
s.size(); // 5
s.count(5); // 1
s.count(6); // 0
auto ret = s.find(1); // 1 검색, ret : 값 1 의 iterator
auto ret = s.insert(6); // 6 등록, ret.first : 등록된 iterator, ret.second : 1 (등록 여부)
auto ret = s.insert(1).first; // 1 등록, ret : 등록된 iterator
auto ret = s.insert(1).second; // 1 등록, ret : 0 (등록 여부)
auto ret = s.erase(2); // 2 삭제, ret : 1 (삭제 여부)
auto ret = s.erase(2); // 2 삭제, ret : 0 (삭제 여부)
auto ret = s.erase(s.begin()); // 맨 앞 element 삭제, ret : 삭제된 element의 다음 iterator
10. Unordered_map
template<
class Key,
class T
class Hash = std::hash<key>,
class KeyEqual = std::equal_to<Key>
class Allocator = std::allocator<std::pair<const Key, T>>
> class unordered_map;
// * size_t: unsigned long long
unordered_map<T, U> htab
unordered_map<T, U, Hash> htab
unordered_map<T, U, Hash, KeyEqual> htab
iteratorhtab.begin(), htab.end() // iteraor 반환 O(1)
U htab[T key] // key 값이 등록X => vaiue = default값(0)으로 자동 등록
bool htab.empty() // 비었으면 true 아님 false O(1)
size_t htab.size() // size 반환 O(1)
void htab.clear() // 초기화 O(n)
size_t htab.count(T key) // x개수 반환 0 or 1 O(1)
iterator htab.find(T key) // x의 iterator반환 없으면 htab.end()반환 O(1)
/* 검색 과정이 상수 시간대라 O(1)로 표현하지만
배열처럼 direct 접근이 아니라 경우에 따라 상수 값이 상당히 커질 수 있음. */
void htab.reserve(int n) // n이상의2^k로bucket_count설정
size_t htab.bucket(T key) // key 값의 bucket 번호 반환
size_t htab.bucket_count() // hash table bucket 개수
size_t htab.bucket_size(intbucket) // bucket의 element 개수
// insert O(1) - 구간은 구간만큼 - 등록된 값 있으면 무시
pair <iterator, bool> htab.insert({T key, U value}) // { 등록된iterator, 성공여부}
void htab.insert(iterator first, iterator last)
// erase O(1) - 구간은 구간만큼
iterator htab.erase(iterator pos)
iterator htab.erase(iterator first, iterator last) // 마지막으로지운element의다음iterator
size_t htab.erase(T key) // 성공여부 0,1
EXAMPLE
// key 값에 대한 value값 추가, pair <key, value> 형태로 존재
unordered_map<int, int> m;
for (int i = 5; i > 0; i--) m.insert({ i, 0 });
// {1,0} {2,0} {3,0} {4,0} {5,0}: 순서 다를 수 있음
for (auto x : m) cout << x.first << ',' << x.second;
for (auto it = m.begin(); it != m.end(); ++it) cout << it->first << ',' << it->second;
m[1] = 3; // {1,3} {2,0} {3,0} {4,0} {5,0}
m[6]; // 등록되어 있지 않은 key값이면 value=0으로 자동 insert : {6, 0}
m[7]++; // key값 7이 등록되어 있지 않으므로 value=0으로 insert 후, value 1 증가 : {7, 1}
m.insert({ 1, 4 }); //// key값 1은 이미 등록되어 있으므로 무시
// m : {1,3} {2,0} {3,0} {4,0} {5,0} {6,0} {7,1}'C++ > STL' 카테고리의 다른 글
| [STL] Set, Map (0) | 2025.06.17 |
|---|---|
| [STL] Priority Queue(2) - Lazy Update (1) | 2025.06.17 |
| [STL] Priority Queue(1) - heap, sort, compair 설정 (0) | 2025.06.16 |
| [STL] Stack, Queue (0) | 2025.06.13 |
| [STL] String (0) | 2025.06.13 |


