해시 테이블은 또는 해시 맵은 키를 값에 매핑할 수 있는 구조인, 연관 배열 추상 자료형(ADT)을 구현하는 자료구조다. 가장 큰 특징은 대부분의 연산이 분할 상환 분석에 따른 시간 복잡도가 O(1)이라는 점이다. 그리고 이를 적용하기 위해 필요한 함수가 바로 해시 함수이다.
1 |
|
-
성능 좋은 해시 함수들의 특징
- 해시 함수 값 충돌의 최소화
- 쉽고 빠른 연산
- 해시 테이블 전체에 해시 값이 균일하게 분포
- 사용할 키의 모든 정보를 이용하여 해싱
- 해시 테이블 사용 효율이 높을 것
여기서 고려해야할 문제중 하나인 충돌을 알아보자
먼저 비둘기집 원리라는게 있다.
1 |
|
비둘기집 원리처럼 해시함수는 임의크기 데이터(n개 데이터)를 고정 크기(m개 컨테이너)로 매핑하기 때문에 고정 크기가 아무리 크더라도 충돌할 수 있는 확률이 아예 0일 수는 없는 것이다.
그럼 충돌이 발생하면 어떻게 처리해야 할까?
충돌을 해결하는 방법엔 크게 2가지가 있다.
1 |
|
개별 체이닝
1 |
|
원리를 순서로 나타내면 아래와 같다.
- 키의 해시 값을 계산한다.
- 해시 값을 이용해 배열의 인덱스를 구한다.
- 같은 인덱스가 있다면(충돌) 연결 리스트로 연결한다.
오픈 어드레싱
1 |
|
오픈 어드레싱 방식 중 가장 간단한 방식인 선형 탐사 방식을 순서로 나타내면 아래와 같다.
- 키의 해시 값을 계산한다.
- 해시 값을 이용해 배열의 인덱스를 구한다.
- 같은 인덱스가 있다면(충돌) 해당 위치부터 다음위치로 이동하면서 순차적으로 탐사를 진행한다.
- 가장 가까운 비어있는 위치를 탐사해 새 키를 삽입한다.
선형 탐사는 비교적 개별 체이닝 방식보다 성능이 좋은편이나 해시 테이블에 저장되는 데이터들이 고르게 분포되지 않고 뭉치는 경향의 문제점이 있다.
그리고 오픈 어드레싱 방식 자체는 연결리스트로 추가할 수 없기 때문에 지정된 슬롯의 개수 이상은 저장할 수 없는 문제점이 있다.
이러한 문제 때문에 로드 팩터 비율이 넘어서게 되면 또 다른 버킷을 생성한 후 여기에 새롭게 복사하는 리해싱 작업이 일어난다.
1 |
|
각 언어별 해시 테이블 적용 방식
언어 | 방식 |
---|---|
C++ | 개별 체이닝 |
자바 | 개별 체이닝 |
Go | 개별 체이닝 |
루비 | 오픈 어드레싱 |
파이썬 | 오픈 어드레싱 |