1. 개념
해시
- 임의의 크기를 가진 데이터(Key)를 고정된 크기의 데이터(Value)로 변화시켜 저장하는 것
- 키에 대한 해시값을 사용하여 값을 저장하고 키-값 쌍의 갯수에 따라 동적으로 크기가 증가하는 associate array 이다
- 키에 대한 해시값을 구하는 과정을 hashing(해싱)이라고 한다.
- 해시값 자체를 index로 사용하기 때문에 평군 시간복잡도가 O(1) 로 매우 빠르다
→ 해시 태이블의 크기에 상관없이 데이터에 빠르게 접근할 수 있다.
→ 모든 데이터를 살피지 않아도 검색과 삽입/삭제를 빠르게 실행할 수 있다.
해시함수
- 위에 설명한 것과 같이 키에 대한 해시값을 만드는 함수(알고리즘)
- 계산이 복잡하지 않고 키값에 대해 중복없이 해시값을 고르게 만들어 내는 함수가 좋은 함수 (충돌이 일어나지 않을수록 좋다)
- 대표적으로 나눗셈법(Division Method)와 곱셉법(Multiplication Method)이 있다
좋은 해시함수의 조건은 Simple uniform hash 함수를 만드는 것으로, 조건은 다음과 같다.
- 계산된 해쉬값들은 0부터 배열의 크기-1 사이의 범위를 '동일한 확률'로 골고루 나타날 것.
→ 충돌이 일어날 확률이 적어진다. - 각각의 해쉬값들은 서로 연관성을 가지지 않고 독립적으로 생성될 것.
→ 해쉬값들이 서로 연관이 있을 경우 연관성이 있으면 해당 해쉬값이 등장하는 패턴이나 순서가 존재 할 수 있고, 이는 반복적인 충돌을 일으 킬 확률이 있기 때문에 독립적으로 생성되도록 한다.
2. 충돌 (Collusion)
해시테이블은 '충돌'이 일어날 수 있다는 문제점이 있다.
충돌이란 키에 대한 해시값이 같은 경우, 즉 사용하고자하는 해시 버킷이 이미 사용중인 경우를 말한다.
이를 방지하기위한 방법이 있으나, 충돌을 '완전히' 방지하기는 어렵다는 점이 해시의 한계이다.
충돌 해결을 위한 방법들
- Chaining
- 충돌이 일어날 경우 데이터들을 포인터를 이용해 서로 링크드 리스트(체인)형태로 연결하는 것
- 충돌을 허용하되, 최소화하는 방식에 가깝다.
- key값을 포인터로 이어서 연결
- 최악의 경우 모든 데이터가 같은 해시값을 가져 O(n)의 복잡도를 가짐
- JDK 라이브러리에 구현된 HashMap 은 chaining 방식을 사용하며 해당 버킷의 길이에 따라 LinkedList에서 Tree로 변경될수 있다
- Open Addressing
- 모든 데이터를 테이블에 저장하는 방법
- 사용하려는 해시 버킷(테이블)이 이미 사용중인 경우 다른 버킷을 사용
- 포인터를 쓰지 않아 오버헤드를 방지할 수 있고 데이터가 적을 경우 연속된 공간에 인자를 저장하기 때문에 공간 효율이 높다
- 포인터가 필요없어 구현이 훨씬 용이해졌으며, 포인터 접근에 필요한 시간이 없어졌기 때문에 큰 성능향상이 있다.
- 하지만 테이블의 크기가 커질수록 장점이 사라진다
- Linear Probing
- 충돌이 발생한다면 바로 다음 인덱스에 데이터를 저장하는 방식 다음으로 이동한 이후에도 충돌이 발생했다면 또 다시 바로 다음인덱스에 저장
-
- Quadratic Probing
- 충돌시 i^2 만큼씩 이동
- Double Hashing
- Quadratic probing의 secondary clustering를 해결하기 위해서 사용하는 방법이다.
- 원리는 간단한데, 해쉬 함수를 해쉬 함수 2개로 구성하는 것이다.
- Quadratic Probing
참고자료
'CS 공부 > 알고리즘' 카테고리의 다른 글
C++ 알고리즘 문제풀이하며 자주 쓰이는 라이브러리, 함수 정리 (0) | 2021.09.07 |
---|---|
불안정 정렬 sort, 안정정력 stable_sort (0) | 2021.09.03 |
Greedy Algorithm (그리디 알고리즘, 탐욕법) 개념 정리 & 문제 추천 (0) | 2021.04.28 |