탐색 - 5. 해싱(Hashing) 기법
2025. 2. 24. 14:56ㆍ소프트웨어/알고리즘
📌 5. 해싱(Hashing) 기법
5.1 해싱(Hashing)의 개념과 필요성
🔹 해싱(Hashing)이란?
해싱(Hashing)은 데이터를 특정한 키(key)를 기반으로 해시 함수(Hash Function)를 통해 빠르게 검색하는 방법입니다.
해싱 기법은 데이터베이스, 캐싱 시스템, 암호화, 파일 시스템 등 다양한 곳에서 사용됩니다.
💡 해싱의 필요성
- 탐색 속도 향상 → O(1)로 탐색 가능 (이진 탐색 O(log n)보다 빠름)
- 데이터 저장 및 검색이 빠름 → 배열과 달리 특정 값 검색 시 빠르게 접근 가능
- 효율적인 메모리 사용 → 정렬된 배열을 유지할 필요 없이 키를 기반으로 데이터 관리 가능
5.2 해시 함수(Hash Function) 및 충돌 해결 방법
🔹 해시 함수(Hash Function)
해시 함수는 입력 값을 특정 크기의 해시 테이블의 인덱스로 변환하는 함수입니다.
해시 함수가 좋은 경우:
- 균등한 해시 값 분포: 데이터가 특정 영역에 몰리지 않도록 설계
- 빠른 계산: 해시 함수 자체가 느리면 해싱의 의미가 사라짐
예제: 간단한 해시 함수 (모듈로 연산)
index = key % table_size;
위 공식은 키(key)를 테이블 크기로 나눈 나머지를 사용하여 인덱스를 생성하는 방식입니다.
🔹 충돌 해결 방법
해시 함수가 동일한 해시 값을 반환하면 충돌(Collision)이 발생합니다.
이를 해결하는 대표적인 방법:
- 체이닝(Chaining) 기법 → 연결 리스트(Linked List)로 동일한 해시 값을 가지는 데이터를 저장
- 개방 주소법(Open Addressing) → 빈 슬롯을 찾아 데이터를 저장하는 방식
5.3 해시 테이블(Hash Table) 구현
해시 테이블(Hash Table)은 배열과 해시 함수를 조합하여 빠르게 데이터를 검색하는 자료구조입니다.
💡 해시 테이블의 기본 구성 요소
- 해시 함수 → 키를 배열 인덱스로 변환
- 해시 테이블 → 변환된 인덱스에 데이터를 저장
- 충돌 해결 기법 → 충돌이 발생할 경우 데이터를 저장하는 방식
🔍 실습: 간단한 해시 테이블 구현 (C 언어)
1️⃣ struct와 배열을 이용한 간단한 해시 테이블 구현
#include <stdio.h>
#include <stdlib.h>
#define TABLE_SIZE 10
// 해시 테이블 구조체
typedef struct {
int key;
int value;
} HashTable;
// 해시 함수 (키 % 테이블 크기)
int hashFunction(int key) {
return key % TABLE_SIZE;
}
// 해시 테이블에 데이터 삽입
void insert(HashTable table[], int key, int value) {
int index = hashFunction(key);
while (table[index].key != 0) { // 충돌 발생 시 다음 슬롯으로 이동 (개방 주소법)
index = (index + 1) % TABLE_SIZE;
}
table[index].key = key;
table[index].value = value;
}
// 해시 테이블에서 값 검색
int search(HashTable table[], int key) {
int index = hashFunction(key);
while (table[index].key != 0) {
if (table[index].key == key) {
return table[index].value;
}
index = (index + 1) % TABLE_SIZE; // 개방 주소법 충돌 해결
}
return -1; // 찾지 못한 경우
}
int main() {
HashTable table[TABLE_SIZE] = {0}; // 초기화
insert(table, 15, 100);
insert(table, 25, 200);
insert(table, 35, 300);
printf("키 25의 값: %d\n", search(table, 25));
printf("키 35의 값: %d\n", search(table, 35));
printf("키 10의 값: %d\n", search(table, 10)); // 존재하지 않는 값
return 0;
}
📌 실행 결과
키 25의 값: 200
키 35의 값: 300
키 10의 값: -1
💡 개방 주소법(Open Addressing) 방식 사용
충돌이 발생하면 다음 빈 슬롯을 찾아 이동하면서 데이터를 저장하는 방식입니다.
🔍 실습: 충돌 해결을 위한 체이닝(Chaining) 기법 적용
개방 주소법 대신 체이닝(Chaining)을 이용하여 연결 리스트(Linked List)로 충돌을 해결할 수도 있습니다.
#include <stdio.h>
#include <stdlib.h>
#define TABLE_SIZE 10
// 연결 리스트 노드 구조체
typedef struct Node {
int key;
int value;
struct Node* next;
} Node;
// 해시 테이블 (배열 + 연결 리스트)
Node* hashTable[TABLE_SIZE] = {NULL};
// 해시 함수
int hashFunction(int key) {
return key % TABLE_SIZE;
}
// 해시 테이블에 데이터 삽입 (체이닝 방식)
void insert(int key, int value) {
int index = hashFunction(key);
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->key = key;
newNode->value = value;
newNode->next = hashTable[index];
hashTable[index] = newNode;
}
// 해시 테이블에서 값 검색
int search(int key) {
int index = hashFunction(key);
Node* current = hashTable[index];
while (current != NULL) {
if (current->key == key) {
return current->value;
}
current = current->next;
}
return -1; // 찾지 못한 경우
}
// 해시 테이블 출력
void printTable() {
for (int i = 0; i < TABLE_SIZE; i++) {
printf("[%d]: ", i);
Node* current = hashTable[i];
while (current != NULL) {
printf("(%d, %d) -> ", current->key, current->value);
current = current->next;
}
printf("NULL\n");
}
}
// 메모리 해제
void freeTable() {
for (int i = 0; i < TABLE_SIZE; i++) {
Node* current = hashTable[i];
while (current != NULL) {
Node* temp = current;
current = current->next;
free(temp);
}
}
}
int main() {
insert(15, 100);
insert(25, 200);
insert(35, 300);
insert(45, 400); // 충돌 발생
printf("키 25의 값: %d\n", search(25));
printf("키 45의 값: %d\n", search(45));
printf("키 10의 값: %d\n", search(10));
printTable(); // 해시 테이블 출력
freeTable(); // 메모리 해제
return 0;
}
📌 실행 결과
키 25의 값: 200
키 45의 값: 400
키 10의 값: -1
[0]: NULL
[1]: NULL
[2]: NULL
[3]: NULL
[4]: NULL
[5]: (45, 400) -> (35, 300) -> (25, 200) -> (15, 100) -> NULL
...
💡 체이닝 방식 → 해시 충돌이 발생하면 연결 리스트에 데이터를 추가하여 해결
📌 마무리 및 정리
- 해싱(Hashing)은 키를 해시 함수로 변환하여 데이터를 빠르게 검색하는 기법
- 해시 함수(Hash Function)는 키를 인덱스로 변환하는 함수
- 충돌 해결 방법
- 개방 주소법(Open Addressing) → 충돌 시 다음 빈 슬롯 찾기
- 체이닝(Chaining) → 연결 리스트를 이용하여 충돌 해결
- C에서 해시 테이블 구현 방법
- struct와 배열을 이용한 기본 해시 테이블
- 연결 리스트(Chaining) 기법 적용하여 충돌 해결
'소프트웨어 > 알고리즘' 카테고리의 다른 글
탐색 - 7. 깊이 우선 탐색(DFS, Depth First Search) (0) | 2025.02.24 |
---|---|
탐색 - 6. 그래프 탐색 개요 (Graph Search) (0) | 2025.02.24 |
탐색 - 4. 점프 탐색(Jump Search) & 보간 탐색(Interpolation Search) (0) | 2025.02.24 |
탐색 - 3. 이진 탐색(Binary Search) (0) | 2025.02.24 |
탐색 - 2. 선형 탐색(Linear Search) (0) | 2025.02.24 |