반응형

출처 :  https://www.booksr.co.kr/product/9788970509716/

체이닝(Chaining)

체이닝은 개방 주소법(Open Addressing)의 충돌 해결 방법 중 하나로, 충돌이 발생했을 때 같은 해시 값에 해당하는 슬롯에 연결 리스트 등의 자료 구조를 사용하여 여러 개의 항목을 저장하는 방법입니다. 체이닝은 충돌이 발생해도 다른 슬롯에 항목을 저장하지 않고, 같은 슬롯에 여러 항목을 연결하여 저장하는 방식입니다.

체이닝의 작동 방식은 다음과 같습니다:

1. 해시 값에 해당하는 슬롯을 선택합니다.
2. 선택한 슬롯에 이미 항목이 있다면, 해당 슬롯에 연결 리스트 등의 자료 구조를 이용하여 새로운 항목을 추가합니다.
3. 선택한 슬롯에 항목이 없다면, 그곳에 새로운 항목을 저장합니다.

체이닝의 장점은 해시 테이블 내부에서 메모리 낭비를 줄일 수 있다는 점입니다. 충돌이 발생하더라도 같은 슬롯에 여러 항목을 연결하여 저장하므로, 빈 슬롯을 찾는 과정이 필요하지 않습니다.

체이닝의 단점은 해시 테이블의 성능이 연결 리스트의 성능에 크게 의존한다는 점입니다. 긴 연결 리스트가 형성되면 검색 성능이 저하될 수 있습니다. 따라서 체이닝을 사용할 때에는 충돌이 발생할 가능성을 고려하여 연결 리스트의 관리 방식과 성능을 신중하게 고려해야 합니다.

체이닝은 충돌을 해결하는 방법 중 하나로, 해시 테이블 내부의 슬롯 구성과 연결 리스트의 구조에 따라 다양한 방식으로 활용될 수 있습니다.

아래는 책에 나온 코드이다.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 해싱 테이블의 내용을 출력
#define TABLE_SIZE	7	// 해싱 테이블의 크기=소수 

typedef struct {
	int key;
} element;

struct list
{
	element item;
	struct list *link;
};
struct list *hash_table[TABLE_SIZE];
// 제산 함수를 사용한 해싱 함수
int hash_function(int key)
{
	return key % TABLE_SIZE;
}

// 체인법을 이용하여 테이블에 키를 삽입
void hash_chain_add(element item, struct list *ht[])
{
	int hash_value = hash_function(item.key);
	struct list *ptr;
	struct list *node_before = NULL, *node = ht[hash_value];
	for (; node; node_before = node, node = node->link) {
		if (node->item.key == item.key) {
			fprintf(stderr, "이미 탐색키가 저장되어 있음\n");
			return;
		}
	}
	ptr = (struct list *)malloc(sizeof(struct list));
	ptr->item = item;
	ptr->link = NULL;
	if (node_before)
		node_before->link = ptr;
	else
		ht[hash_value] = ptr;
}

// 체인법을 이용하여 테이블에 저장된 키를 탐색
void hash_chain_search(element item, struct list *ht[])
{
	struct list *node;

	int hash_value = hash_function(item.key);
	for (node = ht[hash_value]; node; node = node->link) {
		if (node->item.key == item.key) {
			fprintf(stderr, "탐색 %d 성공 \n", item.key);
			return;
		}
	}
	printf("키를 찾지 못했음\n");
}

// 체인법을 이용하여 테이블에 저장된 키를 탐색
void hash_chain_search(element item, struct list *ht[])
{
	struct list *node;

	int hash_value = hash_function(item.key);
	for (node = ht[hash_value]; node; node = node->link) {
		if (node->item.key == item.key) {
			fprintf(stderr, "탐색 %d 성공 \n", item.key);
			return;
		}
	}
	printf("키를 찾지못했음\n");
}
void hash_chain_print(struct list *ht[])
{
	struct list *node;
	int i;
	printf("\n===============================\n");
	for (i = 0; i<TABLE_SIZE; i++) {
		printf("[%d]->", i);
		for (node = ht[i]; node; node = node->link) {
			printf("%d->", node->item.key);
		}
		printf("\n");
	}
	printf("===============================\n");
}
#define SIZE 5

// 해싱 테이블을 사용한 예제 
int main(void)
{
	int data[SIZE] = { 8, 1, 9, 6, 13 };
	element e;

	for (int i = 0; i < SIZE; i++) {
		e.key = data[i];
		hash_chain_add(e, hash_table);
		hash_chain_print(hash_table);
	}
	for (int i = 0; i < SIZE; i++) {
		e.key = data[i];
		hash_chain_search(e, hash_table);
	}
	return 0;
}

 

해싱의 성능 분석

출처 :&nbsp;https://www.google.com/url?sa=i&url=https%3A%2F%2Fslidesplayer.org%2Fslide%2F15079520%2F&psig=AOvVaw3FuoqZZXbUW0Wn36CWZhwD&ust=1691835449997000&source=images&cd=vfe&opi=89978449&ved=0CA4QjRxqFwoTCMjcyaqw1IADFQAAAAAdAAAAABAD

해싱의 성능을 분석하기 위해서는 주로 다음과 같은 요소들을 고려하고 측정해야 합니다:

1. 로드 팩터 (Load Factor): 로드 팩터는 해시 테이블에 저장된 항목의 수를 전체 슬롯 수로 나눈 값입니다. 즉, 로드 팩터 = 저장된 항목 수 / 전체 슬롯 수 입니다. 로드 팩터가 증가하면 충돌이 더 자주 발생할 가능성이 있습니다. 이를 통해 해시 테이블의 현재 사용 상태를 알 수 있습니다.

2. 충돌 발생 빈도와 처리: 충돌은 해싱에서 불가피한 현상입니다. 성능 분석에서는 충돌이 얼마나 자주 발생하는지와 이를 어떻게 처리하는지를 확인해야 합니다. 충돌이 너무 빈번하게 발생한다면 성능 저하의 원인이 될 수 있습니다.

3. 해시 함수 성능: 사용하는 해시 함수의 성능은 해싱의 효율성에 큰 영향을 미칩니다. 좋은 해시 함수는 입력 데이터를 골고루 분산시키고 충돌을 최소화해야 합니다.

4. 충돌 해결 방법 성능: 개방 주소법이나 체이닝과 같은 충돌 해결 방법의 성능도 중요합니다. 각각의 방법이 어떻게 충돌을 처리하며 어떤 성능 특성을 가지는지를 고려해야 합니다.

5. 해시 테이블 크기: 해시 테이블의 크기가 얼마나 큰지도 성능에 영향을 미칩니다. 해시 테이블이 너무 작으면 충돌이 더 빈번하게 발생할 수 있고, 너무 크면 메모리 낭비가 발생할 수 있습니다.

6. 해시 함수의 분포 특성: 사용하는 데이터의 특성에 따라 해시 함수의 분포 특성을 고려해야 합니다. 입력 데이터의 분포에 따라 충돌이 빈번하게 발생하거나 특정한 슬롯에 항목이 몰릴 수 있습니다.

7. **검색 및 삽입 연산 성능**: 해시 테이블에서의 검색과 삽입 연산 성능을 측정해야 합니다. 충돌이 적절히 처리되고 해시 함수가 잘 설계되어 있다면 검색과 삽입이 효율적으로 이루어져야 합니다.

성능 분석은 실제 데이터를 사용하여 실험을 통해 측정하거나 모델링하여 예측하는 방식으로 이루어질 수 있습니다. 해싱의 성능을 최적화하기 위해서는 여러 가지 요소들을 조합하여 최적의 해시 함수, 충돌 해결 방법, 해시 테이블 크기 등을 선택하고 조정하는 것이 필요합니다.

 

해싱의 응용 분야

해싱은 다양한 컴퓨터 과학 및 정보 기술 분야에서 널리 사용되는 중요한 기술입니다. 주요한 응용 분야는 다음과 같습니다:

1. 데이터베이스 관리 시스템: 해싱은 데이터베이스 내의 레코드를 검색하고 관리하는 데 사용됩니다. 해시 인덱스를 통해 레코드에 빠르게 접근하여 데이터 검색 속도를 향상시킵니다.

2. 캐시 관리: 해싱은 캐시 메모리 관리에도 사용됩니다. 데이터의 중복 액세스를 최소화하여 캐시 메모리를 효율적으로 활용하고 데이터 액세스 속도를 높이는 역할을 합니다.

3. 암호화 및 보안: 해시 함수는 암호화와 데이터 무결성 검증에 사용됩니다. 패스워드를 해시화하여 저장하거나, 디지털 서명을 생성하거나, 데이터 변조를 감지하는 데 활용됩니다.

4. 검색 및 검색 엔진: 해시 함수를 이용한 해시 테이블은 검색 및 검색 엔진에서 효율적인 데이터 관리를 위해 사용됩니다.

5. 분산 시스템: 해싱은 데이터를 분산하여 저장하고 처리하는데 활용됩니다. 데이터를 일정하게 분산시키는 해시 함수를 통해 데이터베이스나 파일 시스템을 분산 환경에서 효과적으로 관리할 수 있습니다.

6. 알고리즘 및 자료 구조: 해싱은 다양한 알고리즘과 자료 구조에서 활용됩니다. 해시 맵, 집합, 집합 연산, 유일성 검사 등을 구현하는 데 사용됩니다.

7. 게임 개발: 게임에서 오브젝트나 캐릭터를 식별하고 관리하는 데에도 해싱이 사용됩니다.

8. 네트워크 라우팅: 해싱은 로드 밸런싱이나 네트워크 트래픽 관리에 사용됩니다.

9. 컴파일러 및 해석기: 심볼 테이블을 구현하거나 심볼의 스코프를 관리하는 데에도 해싱이 활용됩니다.

해싱은 데이터를 빠르게 검색하고 관리하기 위한 핵심적인 기술로서 다양한 분야에서 활용되며, 데이터 처리와 성능 향상을 위한 매우 중요한 역할을 합니다.

 

관심이 있으신 분들에게 유용한 정보였길 바라며

이렇게 C언어로 쉽게 풀어쓴 자료구조가 끝나게 되었습니다.또, 다른 유익한 정보로 돌아오겠습니다. 다들 고생하셨습니다.

 

반응형

+ Recent posts