哈希冲突的两种解决办法

博客介绍了开散列的两种方法。开放地址法在发生哈希冲突且表未装满时,将key存到表的下一个空位,缺点是易产生数据堆积,降低搜索速率;拉链法把相同关键码元素用单链表连接成桶,头节点存于哈希表,不存在溢出问题。

1.开散列(开放地址法)

思想:发生哈希冲突时,如果哈希表未装满,可以把key存放到表的“下一个”空位中去。即 pos++

缺点:所有冲突连在一起易产生数据堆积,占用别的数据的关键码,导致搜索速率下降

 

#pragma once
#include <stdio.h>
#include <malloc.h>
#include <assert.h>

typedef int KeyDataType;
typedef int ValueDataType;

typedef enum State
{
	EMPTY,
	DELETE,
	EXITS,
}State;

typedef struct HashData
{
	KeyDataType key;
	ValueDataType value;
	State state;
}HashData;

typedef struct HashTable
{
	HashData* _tables;
	size_t size;
	size_t capacity;
}HashTable;

void HashTableInit(HashTable* ht,size_t capacity);
HashData* HashTableFind(HashTable* ht, KeyDataType key);
int HashTableInsert(HashTable* ht, KeyDataType key, ValueDataType value);
int HashTableRemove(HashTable* ht, KeyDataType key);
void HashTableDestory(HashTable* ht);
void PrintHashTable(HashTable* ht);


#include "HashTable.h"

int HashFunc(HashTable* ht, KeyDataType key)
{
	assert(ht);
	return (key)%(ht->capacity);
}

void HashTableInit(HashTable* ht, size_t capacity)
{
	ht->_tables = (HashData*)malloc(sizeof(HashData)* capacity);
	ht->size = 0;
	ht->capacity = capacity;
	for (size_t i = 0; i < ht->capacity; i++)
	{
		ht->_tables[i].state = EMPTY;
	}
}

int HashTableInsert(HashTable* ht, KeyDataType key, ValueDataType value)
{
	assert(ht);
	if ((ht->size * 10/ ht->capacity)>7)                //负载因子大于0.7,扩容
	{
		ht->_tables = (HashData*)realloc(ht->_tables, sizeof(HashData)* 2 * (ht->capacity));
		ht->capacity = 2 * (ht->capacity);
	}
	int pos = HashFunc(ht, key);
	while (ht->_tables[pos].state == EXITS)
	{
		pos++;
	}
	ht->_tables[pos].key = key;
	ht->_tables[pos].value = value;
	ht->_tables[pos].state = EXITS;
	ht->size++;
	return 1;
}

HashData* HashTableFind(HashTable* ht, KeyDataType key)
{
	assert(ht);
	int pos = HashFunc(ht, key);
	while (ht->_tables[pos].state != EMPTY)
	{
		if (ht->_tables[pos].key == key)
		{
			printf("找到了!\n");
			return &(ht->_tables[pos]);
		}
		else
		{
			pos++;
		}
	}
	printf("没找到!\n");
	return NULL;
}

int HashTableRemove(HashTable* ht, KeyDataType key)
{
	assert(ht);
	HashData* del = HashTableFind(ht, key);
	if (del)
	{
		del->state = DELETE;
		printf("删除成功!\n");
		ht->size--;
		return 1;
	}
	return 0;
}

void HashTableDestory(HashTable* ht)
{
	assert(ht);
	free(ht->_tables);
	ht->_tables = NULL;
	ht->capacity = 0;
	ht->size = 0;
}

void PrintHashTable(HashTable* ht)
{
	assert(ht);
	size_t pos = 0;
	for (pos = 0; pos < ht->capacity; pos++)
	{
		if (ht->_tables[pos].state == EXITS)
		{
			printf(" %d : %d\n", ht->_tables[pos].key, ht->_tables[pos].value);
		}
	}
}

2.开散列(拉链法)

思想:将具有相同关键码的元素用单链表连接起来,每一个链称为一个桶

每个单链表的头节点存在哈希表中。不存在溢出

#pragma once
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
typedef int KeyData;
typedef int ValueData;

typedef enum State
{
	EXITS,
	EMPTY,
	DELETE,
}State;

typedef struct HashData
{
	KeyData _key;
	ValueData _value;
	State _state;
	struct HashData* _next;
}HashData;

typedef struct HashTable
{
	HashData* _data;
	size_t capacity;
}HashTable;

void HashTableInit(HashTable* ht, size_t capacity);
HashData* HashTableFind(HashTable* ht, KeyData key);
int HashTableInsert(HashTable* ht, KeyData key, ValueData value);
int HashTableRemove(HashTable* ht, KeyData key);
void HashTableDestory(HashTable* ht);
void PrintHashTable(HashTable* ht);


#include "HashTable.h"
size_t HashFunc(HashTable* ht, KeyData key)        //计算位置
{
	assert(ht);
	return key%(ht->capacity);
}

HashData* BuyNode(KeyData key, ValueData value)
{
	HashData* newNode = (HashData*)malloc(sizeof(HashData));
	newNode->_key = key;
	newNode->_value = value;
	newNode->_next = NULL;
	newNode->_state = EXITS;
	return newNode;
}

void HashTableInit(HashTable* ht, size_t capacity)
{
	assert(ht);
	ht->_data = (HashData*)malloc(sizeof(HashData)*capacity);
	ht->capacity = capacity;
	for (size_t i = 0; i < capacity; i++)           //将哈希表中的状态都初始化为空
	{
		ht->_data[i]._state = EMPTY;
		ht->_data[i]._next = NULL;
	}
}

HashData* HashTableFind(HashTable* ht, KeyData key)
{
	assert(ht);
	size_t pos = HashFunc(ht, key);
	HashData* cur = &(ht->_data[pos]);
	if (cur->_state == EMPTY)
		return NULL;
	while (cur->_next != NULL && cur->_next->_state == EXITS)    //遍历到一个桶的最后一个元素
	{
		if (cur->_next->_key == key)
		{
			printf("找到了!\n");
			return cur->_next;
		}
		cur = cur->_next;
	}
	printf("没找到!\n");
	return NULL;
}

int HashTableInsert(HashTable* ht, KeyData key, ValueData value)
{
	assert(ht);
	size_t pos = HashFunc(ht, key);
	if (ht->_data[pos]._state == EMPTY)                 //插入第一个元素
	{
		ht->_data[pos]._next = BuyNode(key, value);
		ht->_data[pos]._state = EXITS;
		return 1;
	}
	else
	{
		HashData* cur = &(ht->_data[pos]);
		while (cur->_next && cur->_next->_state==EXITS)
		{
			cur = cur->_next;
		}
		cur->_next = BuyNode(key, value);           //挂到桶最后
		return 1;
	}

}

int HashTableRemove(HashTable* ht, KeyData key)
{
	assert(ht);
	HashData* cur = HashTableFind(ht, key);
	if (cur == NULL)
	{
		printf("删除失败!\n");
		return 0;
	}
	if (cur->_next)							//删除桶的非尾节点
	{
		HashData* del = cur->_next;
		HashData* next = del->_next;
		cur->_key = del->_key;
		cur->_value = del->_value;
		cur->_next = del->_next;
		cur->_state = del->_state;
		free(del);
		del = NULL;
		printf("删除成功!\n");
		return 1;
	}
	else                                     //删除桶的尾节点
	{
		cur->_state = DELETE;
		printf("删除成功!\n");
		return 1;
	}
}

void HashTableDestory(HashTable* ht)
{
	assert(ht);
	size_t i = 0;
	for (i = 0; i < ht->capacity; i++)
	{
		HashData* pos = (&(ht->_data[i]));    
		if (pos->_state == EMPTY)
			continue;
		pos = pos->_next;				//桶中第一个元素
		if (pos != NULL && pos->_state != EMPTY)		//不能释放哈希表中的元素,只能释放桶中的元素
		{
			while (pos)                        //销毁一个位置挂的桶
			{
				HashData* del = pos->_next;
				free(pos);
				pos = NULL;
				pos = del;
			}
		}
	}
	free(ht->_data);                 //销毁哈希表
	ht->_data = NULL;
	ht->capacity = 0;
}

void PrintHashTable(HashTable* ht)
{
	assert(ht);
	size_t i = 0;
	for (i = 0; i < ht->capacity; i++)
	{
		HashData* pos = &(ht->_data[i]);
		if (pos->_state == EXITS)
		{
			while (pos->_next)
			{
				printf("%d : %d\n", pos->_next->_key, pos->_next->_value);
				pos = pos->_next;
			}
		}
	}
}





 

### 哈希冲突解决方案概述 哈希表是一种高效的数据结构,用于存储键值对。然而,在实际使用中,由于哈希函数的限制或数据分布的原因,可能会出现哈希冲突的现象,即不同的键被映射到同一个位置。为了解决这一问题,有多种方法可供选择[^1]。 以下是几种常见的哈希冲突解决方案: #### 1. 开放寻址法 开放寻址法的核心思想是在发生冲突时,寻找下一个可用的空闲位置来存储数据。具体实现方式包括以下几种: - **线性探测**:当发生冲突时,按照固定的步长(通常是1)依次查找下一个位置,直到找到空闲位置[^3]。 - **平方探测**:与线性探测类似,但步长为平方数序列,例如 \( i^2 \),以减少聚集现象[^3]。 - **双重哈希**:使用第二个哈希函数计算步长,从而避免聚集问题。例如,假设第一个哈希函数为 \( h_1(k) = k \% m \),第二个哈希函数为 \( h_2(k) = c - (k \% c) \),其中 \( c < m \) 是一个常数[^4]。 #### 2. 链地址法 链地址法通过在每个哈希表槽位维护一个链表来解决冲突。当多个键被映射到同一个槽位时,这些键将被存储在该槽位对应的链表中。这种方法的优点是实现简单且易于扩展,但可能增加内存开销。 #### 3. 再哈希法 再哈希法的基本思想是使用多个哈希函数。当发生冲突时,尝试用另一个哈希函数重新计算地址,直到找到空闲位置[^3]。这种方法可以有效减少冲突概率,但需要额外设计多个哈希函数。 #### 4. 公共溢出区 公共溢出区方法哈希表分为两部分:主表和溢出区。主表用于存储大部分数据,而溢出区用于存储冲突的数据。当主表中的某个槽位发生冲突时,冲突的元素会被存储到溢出区[^1]。这种方法可以提高空间利用率,但可能降低查询效率。 #### 5. 完美哈希 完美哈希是一种针对静态数据集的无冲突方案。它通过预先构建哈希函数,确保所有键都能被唯一映射到不同的槽位[^2]。虽然完美哈希在特定场景下非常高效,但它通常不适用于动态数据集。 --- ### 示例代码 以下是一个基于链地址法的哈希表实现示例: ```python class HashTable: def __init__(self, size): self.size = size self.table = [[] for _ in range(size)] def hash_function(self, key): return key % self.size def insert(self, key, value): index = self.hash_function(key) for item in self.table[index]: if item[0] == key: item[1] = value # 更新值 return self.table[index].append([key, value]) # 插入新值 def search(self, key): index = self.hash_function(key) for item in self.table[index]: if item[0] == key: return item[1] return None # 测试 ht = HashTable(5) ht.insert(1, "one") ht.insert(6, "six") print(ht.search(1)) # 输出: one print(ht.search(6)) # 输出: six ``` --- ### 性能权衡 在选择哈希冲突解决方案时,需要综合考虑以下几个方面: - **时间 vs 空间**:开放寻址法通常占用较少的空间,但可能增加查找时间;链地址法则相反。 - **实现复杂度 vs 性能稳定性**:简单的方法如链地址法容易实现,但性能可能受负载因子影响较大;复杂方法如完美哈希则需要更多前期准备。 - **通用性 vs 特殊优化**:通用方法适用于大多数场景,而特殊优化方法(如完美哈希)仅适用于特定场景。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值