Hash Table

Previous Next

TL;DR

자료구조 빈출 유형 !!!




해시 테이블



1. 해시 테이블이란?

img


장점


단점



2. 해시 함수


1) Division Method 나눗셈


2) Multiplication Method 곱셈

\[h(k) = (kA mod 1)xm\]


3) Digit Folding 자릿수 접기


4) Universal hashing 전체 해싱


보안과 해시

5) MD5 (Message-Digest Algorithm)


6) SHA (Secure Hash Algorithm)



3. 충돌 처리 알고리즘

☝️ 여기서 잠깐!

충돌 처리 알고리즘이 왜 필요할까?

해시 함수를 무조건 1:1 로 만드는 것은 Array와 다를바 없고 메모리를 너무 많이 차지하게 된다. 따라서 Collision 을 최소화 하는 방향을 설계하고, 발생하는 Collision에 대비해 어떻게 대응할 것인가를 정하는 것이 더 중요하다.


1. Separate Chaining - 분리 연결법

img




2. Open Addressing - 개방 주소법

img




4. 해시 버킷 동적 확장(Resize)




(Java) Hash Map


Hash Table vs Hash Map

Hash Table Hash Map
병렬 처리를 할 때 = 동기화 (멀티 스레드) 병럴 처리를 하지 않을 때 (단일 스레드)
Null 값 허용하지 않음 Null 값 허용
상대적으로 느리다 상대적으로 빠르다




질문

직접 주소화 방법을 알고 계시나요? 그 방법이 무엇인지, 장점과 단점을 설명해주세요.

직접 주소화 방법은 입력받은 key 값을 그대로 인덱스로 해 value를 저장하는 방법입니다. 키 값으로 바로 접근하기 때문에 속도가 빠르지만, 공간 효율성이 좋지 않고 키 값이 한정된다는 단점이 있습니다.


(추가) 해당 방법의 단점을 개선한 자료구조는 무엇인가요?

Hash Table 입니다. 해시 테이블은 해시 함수를 이용해 해시 값을 인덱스로 해서 value를 저장합니다. 해시 함수를 사용함으로 지정한 해시 테이블 크기만큼으로 키값이 변환되어 공간을 더 효율적으로 사용할 수 있습니다.



해시가 무엇인지, 시간복잡도는 어떻게 되는지 설명해주세요.

해시는 가변 길의의 데이터를 고정 길이의 데이터로 매핑한 값을 의미하고, 해시 테이블의 해시 변환, 검색, 삭제에 걸리는 시간 복잡도는 이론적으로 O(1)입니다.

하지만 만약 모든 해시 값이 하나로 귀결되는 최악의 경우에는 배열과 마찬가지로 O(N)이 됩니다.



해시테이블은 내부적으로 어떻게 작동하나요?

해시 테이블은 해시 함수를 이용해 입력된 값의 해시 값을 인덱스로 삼아 저장하고, 동적 배열로 구현되어 있어 사이즈를 늘릴 수 있습니다.



좋은 해시함수는 무엇인가요?

해시 함수는 인덱스를 설정하는 함수이므로, 좋은 해시 함수는 특정 값에 치우치지 않고 해시값을 고르게 만들어내는 함수입니다.



아까 해시 테이블의 시간복잡도가 최악의 경우 O(n)이라고 했는데 충돌을 해결할 수 있는 방법은 무엇인가요?

우선 해시 함수를 먼저 점검할 것 같습니다. 해시 함수를 수정해 충돌을 최소화할 수 있는지 확인해보고, 그 다음으로 충돌을 대비하기 위해 Chaining 이나 Open Addressing 방법을 사용할 수 있습니다.



충돌 대비를 위한 Chaining 과 Open Addressing 설명

Chaining은 충돌이 된 경우, 링크드 리스트와 같은 자료구조를 사용해 노드를 추가하는 방식입니다.

Open Addressing은 충돌이 된 경우, 빈 해시 테이블에 저장을 하는 방법입니다. 빈 공간을 찾는 방법에는 linear 하거나, quadratic 하게 하는 등의 방법이 있습니다.



Open Addressing에서 선형 조사법, 이차 조사법의 문제점과 해결방안

선형 조사법과 이차 조사법은 데이터들이 뭉쳐서 저장되는 클러스터링 문제를 야기할 수 있습니다. 이를 해결하기 위해 빈 해시 테이블을 찾을 때 또 다른 해시 함수를 사용해 좀 더 거리의 랜덤성을 부여할 수 있을 것 같습니다.



본인이 충돌이 잦은 경우에 해시함수를 사용한다면 어떤 방식으로 해시테이블을 구성할건가요?

만약 해시 테이블이 75% 이상 차 있는 경우라면 테이블 사이즈를 증가해보고, 그래도 문제가 발생한다면 해시 함수를 살펴보거나 해당 데이터를 잘 담을 수 있는 다른 자료 구조가 있는지 찾아볼 것 같습니다.

만약 해시 테이블을 사용해야하는 경우라면 충돌 방지 기법 중 Chaining 을 이진 탐색 트리로 구현해 어떤 경우에도 O(logN) 안에 탐색할 수 있도록 구현할 것 같습니다.



추가 질문 (해시 테이블 외 질문!)

배열과 링크드리스트가 무엇인지 공통점과 차이점을 기준으로 설명해주세요.

배열은 연속된 메모리에 데이터를 저장하는 자료 구조이고, 링크드 리스트는 각 노드가 데이터와 포인터로 구성되어 포인터로 연결되어 있는 자료 구조입니다.

배열과 링크드 리스트 둘 다 데이터를 연속적으로 저장하기 위한 자료 구조입니다. 하지만 배열은 인덱스로 접근이 가능해 탐색이 O(1)로 빠른데 반해 링크드 리스트는 일반적으로 O(N) 시간을 갖습니다.

반면 배열은 크기가 고정적이고 메모리 공간이 낭비되지만 링크드 리스트는 유동적이며 메모리 공간을 낭비하지 않는다는 차이점이 있습니다.


(추가) 배열은 접근 속도가 빠르다고 했는데, 어떻게 그렇게 빠른 접근 속도를 가지나요?

배열은 연속된 메모리에 데이터를 저장하기 때문에 인덱스를 통해서 접근할 수 있어 배열의 특정 위치에 있는 데이터를 한 번에 접근할 수 있습니다.


(추가) 수정할 때 시간복잡도는 어떻게 되나요?

특정 위치의 데이터의 값을 수정한다면 O(1) 이 되고, 삽입과 삭제와 같은 작업이라면 특정 위치에 삽입/삭제 후 나머지 데이터를 뒤로 밀어주거나, 당겨주는 작업이 필요해 일반적으로 O(N) 이 됩니다.


(추가) 배열에서 인덱스 값 어떻게 계산하나요?

int형 배열인 경우에는 4바이트씩 메모리 주소가 떨어지게 되고, 해당 메모리 주소마다 인덱스를 0부터 1씩 증가시켜 계산합니다.


그러면 결국 링크드 리스트는 삽입, 삭제, 탐색 모두에 O(n)의 시간복잡도를 가지게 되는데 왜 굳이 사용하는 걸까요?

배열은 고정된 크기를 갖기 때문에 미리 원소의 개수를 알 수 없을 때 메모리를 효율적으로 사용하기 위해 사용합니다.



(추가) 어떤 경우에 배열을 사용하고 어떤 경우에 링크드 리스트를 사용할까요?

데이터 개수가 정해져있고, 검색이 많이 필요한 경우에는 배열을, 데이터 크기가 정해져 있지 않고 삽입과 삭제가 자주 일어날 때는 링크드 리스트를 사용하면 좋을 것 같습니다.



배열과 링크드 리스트는 각각 메모리의 어떤 영역에 저장될까요? (이어서 메모리 영역 묻기)

배열은 컴파일 단계에서 stack 이나 data영역에 메모리 할당이 일어나고, 링크드 리스트는 런타임에 새로운 노드가 추가될 때마다 heap 영역에서 할당되어 저장됩니다.

만약 동적 배열이라면 heap 영역에 할당될 것입니다.



Priority Queue에 대해서 들어보셨나요? 어떻게 구현하나요?

네, 큐는 먼저 들어온 순서대로 나가는 FIFO 형 자료구조 입니다. 우선순위 큐는 들어온 순서대로가 아닌 우선 순위가 높은 순서대로 나가는 형태의 자료구조입니다.

구현은 완전 이진트리의 일종인 heap으로 구현합니다.



(추가) 왜 그렇게 구현하나요?

최소 힙이나 최대 힙을 사용하면 데이터를 넣는 것만으로도 이미 우선 순위 큐에 맞게 정렬이 되기 때문입니다.




References