0. map vs hash_map (std::unordered_map)
(1) map ( C#의 dictionary와는 다름 )
- 균형 이진 트리의 트리 구조, 레드-블랙 트리로 구성되어 있음
- 키를 기준으로 자동으로 정렬해서 저장
- 시간 복잡도 : O( log N )
(2) hash_map (std::unordered_map, C#의 dictionary)
- 메모리를 대가로 속도를 이점으로 가져감
- 삽입 순서에 따라서 저장
- 해시 충돌이 없으면 속도가 map에 비해 굉장히 빠름
- 시간 복잡도 : 데이터의 추가, 탐색, 삭제가 상수의 시간 복잡도를 가짐 => O(1)
1. 해시 테이블 ; unordered_map
키-값 쌍으로 이루어져 있으므로 특정 값에 접근하려면 키만 알면 됨.
그러나 키-값 쌍이 많아지면 메모리 용량을 많이 차지함.
(1) 테이블(Hash 'Table')
void TestTable()
{
struct User
{
int userId = 0; // 0 ~ 999
string username;
};
vector<User> users;
users.resize(1000);
// 777번 유저 정보 대입
users[777] = User{ 777, "Guest" };
// 777번 유저의 이름 찾기
string name = users[777].username;
cout << name << endl;
}
키를 이용하여 배열에 임의 접근할 수 있다.
(2) 해시('Hash' Table)
해시 알고리즘에 따라서 주어진 데이터를 어떤 크기의 값으로 변환 함.
해시 값을 알아도 원래 데이터를 알아낼 수는 없음(해시 알고리즘을 알아내지 못하는 이상).
-> 비밀번호 찾기를 하면 원래 비밀번호를 알려주지 않고 새로운 비밀번호로 바꾸는 이유
void TestHash()
{
struct User
{
int userId = 0; // 0 ~ 999
string username;
};
// 1000개의 버킷 사용
vector<User> users;
users.resize(1000);
const int userId = 123456789; //123456789번째 유저
int key = (userId % 1000); // 해시 알고리즘
// 유저 정보 대입
users[key] = User{ userId, "Guest" };
// 유저의 이름 찾기
User& user = users[key];
if (user.userId == userId)
{
string name = user.username;
cout << name << endl;
}
}
2. 해시 충돌(Hash Collision)
서로 다른 데이터가 동일한 해시 값을 가질 때(키 중복) 이 두 데이터는 같은 버킷에 담긴다(한 지붕 두 가족?).
이로 인해 데이터 검색, 삽입, 수정의 속도가 저하된다.
- 조사법(Probing)
해시 충돌이 발생한 버킷에 수행하는 방법. 다른 빈 버킷을 찾아 데이터를 저장(중복된 데이터를 분리)하려 시도한다.
(1) 선형 조사법 (Linear Probing)
충돌이 발생한 버킷의 바로 다음 버킷을 탐색한다. 빈 버킷이 나올 때까지 반복한다.
(2) 이차 조사법 (Quadratic Probing)
충돌이 발생한 버킷에서 이차 함수만큼 떨어진 버킷을 탐색한다. 빈 버킷이 나올 때까지 반복한다.
- 체이닝(Chaining)
각 버킷을 연결 리스트(혹은 배열)로 관리함.
중복되는 해시 값이 등장하면 연결 리스트(혹은 배열)에 추가하는 방법으로 버킷에 저장.
void TestChaining()
{
struct User
{
int userId = 0; // 0 ~ 999
string username;
};
// 1000개의 버킷 사용
vector<vector<User>> users;
users.resize(1000);
const int userId = 123456789; //123456789번째 유저
int key = (userId % 1000); // 해시 알고리즘
// 유저 정보 대입
users[key].push_back(User{ userId, "Guest" });
vector<User>& bucket = users[key]; // 각 버킷은 users 이중 배열로 이루어져 있음
// 버킷의 유저들을 각각 대입
for (User& user : bucket)
{
if (user.userId == userId) // 입력된 userId와 일치하면
{
string name = user.username; // username 출력
cout << name << endl;
}
}
}
위 코드에서 각 버킷은 users 이중 배열로 되어 있다. users 이중 배열 내의 user 들의 userId를 입력된 userId값과 비교해서 일치하면 username을 출력하게 한다.
시간 복잡도 : O( N ), 상수의 시간 복잡도
'자료구조와 알고리즘 > 정렬 & 해시 테이블' 카테고리의 다른 글
5-3. 퀵 정렬(Quick Sort) (0) | 2023.07.20 |
---|---|
5-2. 힙 정렬, 병합 정렬 (0) | 2023.07.19 |
5-1. 버블 정렬, 선택 정렬, 삽입 정렬 (0) | 2023.07.19 |