특징
1. 자동적으로 정렬되지 않는다.
2. hash 사용으로 bucket 으로 인해 메모리 사용량 증가
3. insertion, deletion, find O(1)에 수행, 다만 충돌이 너무 많이 일어나면 O(N)
(그러면 기존 set 보다 더 느리게 수행될 수 있다는 말이다.)
4. hash 자료구조를 사용한다.
원소의 개수가 많을수록 해시 충돌이 일어날 확률도 많아지게 되므로
경우에 따라 사용하자.
원소의 개수가 적고 빠른 성능 : unordered_set
원소의 개수가 많고 안정성 : set
Hash function
정의는 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수.
unordered_set 에 특정 value 를 찾거나 넣을 때 해당 value 를 hash function 에 넣고 나온 리턴 값인
hash value 를 얻는다.
기본적으로 hash value 는 bucket 에 들어가는 index 보다 크기 때문에 bucket 의 size 로 나머지 연산을 하여서 최종 index 를 얻고 그 bucket[index] 에 해당 value 를 저장하게 된다.
1. value -> hash function -> hash value(해시 값)
2. hash value % bucket count = index
3. bucket[index] 에 linked list 로 value 를 넣어준다.
이때 서로 다른 value 라도 우연히 hash value 가 같을 수 있는데
이를 hash collision(해시 충돌)이라고 한다.
O(1) ??
insertion, deletion, find 가 어떻게 O(1)이 나올까?
문자열 "abc" 를 unordered_set 에 삽입한다고 하면.
1. "abc" 는 hash function 으로 리턴 값인 hash value : 23103023124 를 얻는다. O(1)
2. hash value % bucket count = 4 (index) O(1)
3. bucket[4] -> "abc" (Linked List에 삽입은 O(1))
따라서 insertion 은 O(1) 에 가능하다.
문자열 "abc" 를 unordered_set 에서 찾거나 삭제한다면?
1. "abc" 는 hash function 으로 리턴 값인 hash value : 23103023124 를 얻는다. O(1)
2. hash value % bucket count = 4 (index) O(1)
3. bucket[4] 에서 "abc" 찾는다. O(1)
따라서 find, deletion 도 O(1) 에 가능하다.
그런데, element 가 너무 많아지게 되면 hash collision 이 빈번하게 발생하여
서로 다른 값임에도 불구하고 같은 bucket 에 연결된다면 O(N) 이 아닐까?
element 가 많아지게 되면 hash 자체 내부에서 rehashing 이 일어나 bucket count 를 늘려 O(1) 에 수행할 수 있게 변경된다. 다만 rehashing 의 경우 O(N) 이 소요되기 때문에 이를 막기 위해 선언할 때
reserve 를 활용하여 rehashing 의 빈도수를 줄일 수 있다.
#include <iostream>
#include <string>
#include <unordered_set>
using namespace std;
int main()
{
unordered_set<string> s;
s.insert("abc");
s.insert("def");
s.insert("ghi");
s.insert("jkl");
// string(input) -> hash function -> hash value
cout << "abc: " << hash<string>{}("abc") << endl;
cout << "def: " << hash<string>{}("def") << endl;
cout << "ghi: " << hash<string>{}("ghi") << endl;
cout << "jkl: " << hash<string>{}("jkl") << endl << endl;
// hash value % bucket_count -> index(hash table mapping)
cout << "bucket count : " << s.bucket_count() << endl;
cout << "abc bucket : " << s.bucket("abc") << endl;
cout << "def bucket : " << s.bucket("def") << endl;
cout << "ghi bucket : " << s.bucket("ghi") << endl;
cout << "jkl bucket : " << s.bucket("jkl") << endl << endl;
// 더 많은 input이 들어오면, rehashing이 발생한다.
// rehashing 은 O(N)이 소모되므로,
// 이를 방지하고자 할 때 미리 할당해주는 reserve 를 사용한다.
s.insert("dasq");
s.insert("dasdawq");
s.insert("rdewq");
s.insert("vkbas");
s.insert("treq");
s.insert("gfoas");
s.insert("bdea");
s.insert("dela");
s.insert("jgela");
cout << "bucket count : " << s.bucket_count() << endl;
return 0;
}
abc: 440920331
def: 3310976652
ghi: 992499321
jkl: 3286144074
bucket count : 8
abc bucket : 3
def bucket : 4
ghi bucket : 1
jkl bucket : 2
bucket count : 64
참고:)
'CS > Data Structure' 카테고리의 다른 글
[C++] multimap (0) | 2021.04.28 |
---|---|
[C++] map (0) | 2021.04.28 |
[C++] multiset (0) | 2021.04.12 |
스택의 응용 - 후위 표기식 (0) | 2021.04.09 |
[C++] set (0) | 2021.04.04 |