【c++学习】初识哈希表模拟实现

哈希的概念

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素
时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即
O( l o g 2 N log_2 N log2N),搜索的效率取决于搜索过程中元素的比较次数。
理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。
如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立
映射的关系,那么在查找时通过该函数可以很快找到该元素.。

在这里插入图片描述

如上图即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称
为哈希表(Hash Table)(或者称散列表)。

哈希冲突

在上图,倘若再来一个16,17,14等类似的值经过哈希函数得出的值两个值一模一样会与表中冲突,改现象成为哈希冲突或哈希碰撞。那么讲如何处理呢?

闭散列的模拟实现

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有
空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。那如何寻找下一个空位置
呢?

线性探测

插入与扩容

在这里插入图片描述
如上图,44模10之后会与4模10的位置冲突,那么将其往下一个位置移动直到一个空位置中。
模拟实现:
我们需要一个状态表示,以表示这个空间是否存在值,是否为空,注意的是我们删除值时,并不能真正的删除这个空间,而是给他一个被删除的状态表示来达到效果

enum State
{
	EMPTY,//空
	EXIST,//存在值
	DELETE//被删除的值
};
//扩容问题
if (_n * 10 / _table.size() >= 7)
{
	HashTable<K, V, Hash> newHT(_table.size() * 2); 
	for (auto& e : _table)
	{
		if (e._state == EXIST)
		{
			newHT.Insert(e._kv);
		}
		_table.swap(newHT._table);
	}
}
//插入问题
Hash hs;
size_t hashi = hs(kv.first) % _table.size();
while (_table[hashi]._state == EXIST)
{
	hashi++;
	hashi %= _table.size();
}
_table[hashi]._kv = kv;
_table[hashi]._state = EXIST;
_n++;
return true;

查找

找到空位置则停止,因为在插入线性探测时,会按照顺从此位置依次往后,找到空则插入。相对于查找倘若找到空,则表示后面肯定没有值。

HashDate<K, V>* Find(const K& key)
{
	Hash hs;
	size_t hashi = hs(key) % _table.size();
	while (_table[hashi]._state != EMPTY)
	{
		if (key == _table[hashi]._kv.first
			&& _table[hashi]._state == EXIST)
		{
			return &_table[hashi];
		}
		++hashi;
		hashi %= _table.size();
	}
	//找到空位置,则停止
	return nullptr;
}

删除

只需要将要删除的状态设置为DELETE即可,并不是真正的删除此空间!

bool Erase(const K& key)
{
	HashDate<K, V>* ret = Find(key);
	if (ret)
	{
		ret->_state = DELETE;
		return true;
	}
	else {
		return false;
	}

}

开散列哈希桶的模拟实现

哈希桶

在这里插入图片描述
如上图经过取模得到的值如果相同,那么则链接在他的后面。

插入

找到表中对应的值,进行链接,如果对应的值相同,则往其后继续链接。

bool Insert(const T& data)
{
	Hash hs;
	if (_n == _table.size())
	{
		vector<Node*> newTables(_tables.size() * 2, nullptr);
		for (int i = 0; i < _table.size(); ++i)
		{
			Node cur = _table[i];
			while (cur)
			{
				Node next = cur->_next;
				size_t hashi = hs(data) & newTables.size();
				cur->_next = newTables[hashi];
				newTables[hashi] = cur;
				cur = next;
			}
			_table[i] = nullptr;
		}
		_table.swap(newTables);
	}
	size_t hashi = hs(data) % _table.size();
	Node* newnode = new Node(data);
	newnode->_next = _table[hashi];
	_table[hashi] = newnode;
	++_n;
	return true;
}

查找

找到对应的值,进行遍历他所链接的值找到并返回。

Node* Find(const K& data)
{
	Hash hs;
	size_t hashi = hs(data) % _table.size();
	Node* cur = _table[hashi];
	while (cur)
	{
		if (cur->_data == data)
		{
			return cur;
		}
		cur = cur->_next;
	}
	return nullptr;
}

销毁

这里可以将其链接的值销毁,不许更改状态。
注意的是是否为位置的头结点。

bool Erase(const K& data)
{
	Hash hs;
	size_t hashi = hs(data) % _table.size();
	Node* prev = nullptr;
	Node* cur = _table[hashi];
	while (cur)
	{
		if (cur->_data == data)
		{
			if (prev)
			{
				prev->_next = cur->_next;
			}
			else
			{
				_table[hashi] = cur->_next;
			}
			delete cur;
			--_n;
			return true;
		}
		prev = cur;
		cur = cur->_next;
	}
	return false;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值