哈希表KV形式的二次探测

本文介绍了一种基于key-value形式的哈希表实现方法,并详细解释了如何进行二次探测解决冲突,包括状态数组、仿函数及KV数组的设计,还提供了完整的代码实现与测试案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、key_value形式的哈希表

哈希表主要有两种形式:

(1)key形式的,存入key的值,然后再去查找key的值

(2)key_value形式的,存入key和对应的value的值,然后通过key来查找value的值,主要可以来实现KV字典查找

对于以上两点本博客并不都一一实现,而是仅仅实现KV形式的

二、K形式和KV形式的相互转换

其实库中也有哈希表这两种的实现,其实K的形式可以转换为KV的形式,也就是V设置成自己的K的形式

如图所示:


三、定义状态数组、仿函数和KV数组

1、定义状态数组

由于哈希表中每个位置的传值不知道设置为什么具体的值好并且删除后不知道设置成什么具体的值好(设置为-1万一保存的就是-1呢),所以设置一个状态数组用来,表示每一个为值上的值的状态,初始时设置为EMPTY,当前位置上存在数字设置为EXIST,要删除当前位置上的值设置为DELETE(也为懒删除法)

enum Status  
{  
    EXIST,  
    DELETE,  
    EMPTY,  
};  

2.定义仿函数

定义放函数的作用主要还是提高代码的复用性,对于不同类型的数据,其处理的函数是不同的,而其他地方的代码都是相似的,这样就可以定义一个仿函数专门来处理哈希函数不同的问题

//仿函数
template<class K>
struct DefaulstHashFuncer
{
	size_t operator()(const K& key)
	{
		return key;
	}
};



template<>
struct DefaulstHashFuncer <string>//特化string类型的仿函数
{

	static size_t BKDRHash(const char * str)//<span style="color:#ff0000;">字符串的哈希算法</span>
	{
		unsigned int seed = 131; // 31 131 1313 13131 131313
		unsigned int hash = 0;
		while (*str)
		{
			hash = hash * seed + (*str++);
		}
		return (hash & 0x7FFFFFFF);
	}

	size_t operator()(const string& str)
	{
		return BKDRHash(str.c_str());
	}
};

3、定义KV数组

template<class K, class V>
struct KeyValue
{
public:
	K _key;
	V _value;
public:
	KeyValue(const K& key = K(), const V& value = V())
		:_key(key)
		, _value(value)
	{}

};

四、具体实现

1、函数声明

template<class K, class V, class HashFuncer = DefaulstHashFuncer<K>>
class HashTaable
{
	typedef KeyValue<K, V> KV;
public:
	HashTaable();
	HashTaable(size_t size);
	~HashTaable();

protected:
	bool Insert(const K& key, const V& value);
	bool Find(const K& key);
	bool Remove(const K& key);
	void PrintTable();
	void Swap(HashTaable<K, V>& ht);

protected:
	size_t _HashFuncO(const K& key);
	size_t _HashFunc(size_t prevHash, int i);//哈希函数
	int _Find(const K& key);
	void _CheckCapacity();

protected:
	KV* _tables;
	size_t _size;
	Status* _status;//状态数组
	size_t _capacity;
};

2、具体的实现

(1)默认构造函数

HashTaable()
		:_tables(NULL)
		, _status(NULL)
		, _size(0)
		, _capacity(0)
	{}

(2)构造函数(开辟size大小的空间)

HashTaable(size_t size)
	:_tables(new KV[size])
	, _status(new Status[size])
	, _size(0)
	, _capacity(size)
{
	//for (_status, EMPTY, sizeof(_status)*_size);//枚举类型不能用memsete因为memset是按字节处理
	for (size_t i = 0; i < _capacity; ++i)//若用memset则里面为随机值
	{
		_status[i] = EMPTY;
	}
}

(3)析构函数

~HashTaable()
{
	if (_tables)
	{
		delete[] _tables;
		delete[] _status;
		_tables = NULL;
		_status = NULL;
	}
}

(4)插入数据

bool Insert(const K& key,const V& value)
{
	/*if (_size == _capacity)
	{
		cout << "Full" << endl;
		return false;
	}*/

	_CheckCapacity();

	int i = 1;
	size_t index = _HashFuncO(key);
	//二次探测
	while (_status[index] == EXIST)
	{
		if (_tables[index]._key == key)
		{
			return false;
		}
	index = _HashFunc(index, i++);
	}
	_status[index] = EXIST;
	_tables[index]._key = key;
	_tables[index]._value = value;
	++_size;
	return true;
}


(5)寻找一个数据

bool Find(const K& key)
{
	if (_Find(key) == -1)
	{
		return false;
	}
	return true;
}

(6)移除某个数据

bool Remove(const K& key)
{
	int index = _Find(key);
	if (index!=-1)
	{
		_status[index] = DELETE;
		return true;
	}
	return false;
}

(7)打印哈希表

void PrintTable()
{
	for (size_t i = 0; i < _capacity; ++i)
	{
		if (_status[i] == EXIST)
		{
			printf("[%d];E->", i);
			cout << _tables[i]._key << endl;
		}
		else if (_status[i]==DELETE)
		{
			printf("[%d];D->", i);
			cout << _tables[i]._key;
		}
		else
		{
			printf("[%d];N", i);
			cout << endl;
		}
	}
}

(8)交换数据

void Swap(HashTaable<K,V>& ht)
{
	std::swap(_tables, ht. _tables);
	std::swap(_size, ht._size);
	std::swap(_status, ht._status);
	std::swap(_capacity, ht._capacity);
}


定义的protected函数:
(9)哈希函数初始的位置

size_t _HashFuncO(const K& key)
{
	HashFuncer hf;
	return hf(key) % _capacity;
}

(10)二次探测哈希函数

size_t _HashFunc(size_t prevHash,int i)//哈希函数
{
	return (prevHash + 2 * i - 1) % _capacity;
}
(11)寻找数据(返回下标的位置)
int _Find(const K& key)
{
	size_t index = _HashFunc(key);//默认哈希表中一定是有空余的位置的
	while (_status[index] != EMPTY)
	{
		if (_tables[index] == key&&_status[index] != DELETE)
		{
			return index;
		}
		++index;
		if (index == _capacity)
		{
			index = 0;
		}
	}
	return -1;
}
(11)增容

void _CheckCapacity()
{
	if (_size * 10 >= _capacity * 7)
	{
		HashTaable<K,V> tmp(2 * _capacity);
		for (size_t i = 0; i < _capacity; ++i)
		{
			if (_status[i] == EXIST)//状态为删除时不用管
			{
				tmp.Insert(_tables[i]._key,_tables[i]._value);
			}
		}
		this->Swap(tmp);//冲突改变,相对位置改变
	}
}

五、测试用例

void Test()
{
	//key/value  二次探测  == 》字典
	HashTaable<string, string> ht1(13);
	ht1.Insert("peter", "张老师");
	ht1.Insert("jack", "杰克");
	ht1.Insert("rose", "玫瑰");
	ht1.PrintTable();
}

### 哈希表的数据结构实现及原理 #### 什么是哈希表哈希表是一种通过键值映射来存储和检索数据项的数据结构[^1]。它利用一种称为 **哈希函数** 的方法,将输入的关键字转换成数组索引位置,从而能够高效地完成插入、删除以及查找操作。 #### 哈希表的核心概念 1. **哈希函数**: 这是一个核心组件,负责将关键字转化为对应的数组索引。理想情况下,一个好的哈希函数应该尽可能均匀分布散列后的结果,减少冲突的发生。 2. **冲突解决机制**: 当不同的关键字被映射到同一个索引时会发生冲突。常见的解决方案有开放地址法(线性探测、二次探测)、拉链法等。 3. **负载因子 (Load Factor)**: 负载因子是指当前已存入哈希表中的元素数量与总桶数的比例。当负载因子过高时,通常会触发扩容操作以维持性能稳定。 #### Python 中的哈希表实现 以下是基于拉链法的一个简单哈希表实现: ```python class HashTable: def __init__(self, size=10): self.size = size self.table = [[] for _ in range(size)] def hash_function(self, key): return hash(key) % self.size def insert(self, key, value): index = self.hash_function(key) bucket = self.table[index] found = False for i, kv in enumerate(bucket): k, v = kv if key == k: bucket[i] = (key, value) found = True break if not found: bucket.append((key, value)) def get(self, key): index = self.hash_function(key) bucket = self.table[index] for k, v in bucket: if key == k: return v raise KeyError(f'Key {key} not found') def delete(self, key): index = self.hash_function(key) bucket = self.table[index] for i, kv in enumerate(bucket): k, v = kv if key == k: del bucket[i] return raise KeyError(f'Key {key} not found') ``` 上述代码展示了如何创建一个简单的哈希表类 `HashTable`,其中包含了插入 (`insert`)、获取 (`get`) 和删除 (`delete`) 方法。 #### Redis 中的哈希表设计 在 Redis 中,为了支持高效的动态扩展和收缩功能,采用了双哈希表的设计方案。具体来说,Redis 定义了一个名为 `dict` 的结构体,内部包含两个哈希表 `ht[2]`。这两个哈希表主要用于执行 rehash 操作——即在需要调整容量大小的时候逐步迁移数据而不会阻塞整个程序运行流程[^2]。 #### 性能分析 - 插入/查询时间复杂度:O(1),但在最坏情况下的冲突处理可能会退化至 O(n)。 - 删除操作同样依赖于定位目标节点的过程,因此平均时间为常量级。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值