1. Hash Table
- 해쉬 테이블은 dynamic set을 구현하는 효과적인 방법 중 하나
- 적절한 가정하에서 평균 탐색, 삽입, 삭제시간 O(1)
- 보통 최악의 경우 O(n)
2. Hash Table이란
: 해쉬 함수(hash function) h를 사용하여 키 k를 T[h(k)]에 저장 = 하나의 큰 배열
- h : U -> { 0, 1, ... , m-1 }
여기서 m은 테이블의 크기, U는 모든 가능한 키들의 집합
- 키 k가 h(k)로 해슁되었다고 말함.
index = h(k) -> 배열의 인덱스를 해쉬테이블의 주소라고 말한다
- 즉 각 키에 대한 해쉬함수값을 그 키를 저장할 배열 인덱스로 사용한다.
3. 해쉬 함수의 예
- 모든 키들을 자연수라고 가정 (어떤 데이터든지 자연수로 해석하는 것이 가능)
- 예 : 문자열
- ASCII 코드 : C = 67, L = 76, R = 82, S = 83
- 문자열 CLRS는 (67 128^3) + (76128^2) + (82128^1) + (83128^0) = 141,764,947 (128진수로 가정)
- 해쉬 함수의 간단한 예 :
- h(k) = k % m, 즉 key를 하나의 자연수로 해석한 후 테이블의 크기 m으로 나눈 나머지
- 항상 0 ~ m-1 사이의 정수가 됨
4. 충돌 (Collision)
- 두 개 이상의 키가 동일한 위치로 해슁되는 경우
- 즉 서로 다른 두 키 k1과 k2에 대해서 h(k1) = h(k2)인 상황
- 일반적으로 |U|>>m 이므로 항상 발생 가능 (즉 단사함수가 아님) - 단사함수가 되려면 배열(m)의 크기가 들어올 수 있는 데이터들(U)의 크기보다 크거나 같아야 한다. (일반적으로 담사함수가 될 수 없다)
==> 단사함수 : 1:1 함수(참고 : https://ko.wikipedia.org/wiki/%EB%8B%A8%EC%82%AC_%ED%95%A8%EC%88%98)
- 만약 |K|>m이라면 당연히 발생(여기서 k는 실제 저장된 키들의 집합)
- 충돌이 발생할 경우 대처 방법이 필요
- 대표적인 두 가지 충돌 해결 방법 : chaining과 open addressing
5. (seperate) chaining에 의한 충돌 해결
: 테이블의 각각의 칸에 직접 키를 저장하지 않고 한 칸에 대응(맵핑)되는 모든 키들을 연결리스트(Linked List)를 만들어서 실제 테이블에는 연결 리스트의 첫번째 주소를 저장함.
< Chaining >
- 각각의 키가 모든 슬롯들에 균등한 확률로 (equally likely) 독립적으로 (independently) 해슁된다는 가정
- 성능 분석을 위해서 주로 하는 가정임
- hash 함수는 deterministic (= 결정론 | 해쉬 함수는 랜덤함수가 아니다 => 같은 key에 대한 해쉬함수 값은 같아야 한다 ) 하므로 현실에서는 불가능
- universal hashing - randomize된 hashing function을 사용하는 기법
- 모든 가능한 키들의 집합 U가 있을때 테이블 안에 저장될 키들을 random selection 해서 hash table에 저장 (key가 랜덤이고 해쉬함수가 랜덤은 아님)
- Load factor a = n / m :
- n : 테이블에 저장될 키의 개수
- m : 해쉬테이블의 크기, 즉 연결리스트의 개수
- 각 슬롯에 저장된 키의 평균 개수
- 연결리스트 T[j]의 길이를 n(j)라고 하면 E[n(j)] = a (SUHA가 성립하면 각각의 키들이 각각의 슬롯에 맵핑될 확률이 다 동일하다는 뜻)
- 만약 n = O(m)이면 평균 검색시간은 O(1) -> 그러니까 SUHA가 성립해야 O(1)인것 (가설임)
Open Addressing에 의한 충돌 해결
해시 테이블은 해시 함수와 배열을 결합해서 만듬. 어떤 항목과 다른 항목의 관계를 모형화 하는데 좋다. 보통 (웹 서버 등에서) 데이터 캐싱하는데 사용. 중복 잡아내는데도 뛰어나다