【C++】开散列模拟哈希表

开散列(链地址法)

  • 原理:
    • 本质是链表集合(vector + list),将发生冲突的元素挂在同一个链表中。哈希表中没有直接存储元素,而存储的是链表首节点的地址
  • 映射关系:通过对元素 关键字(key) 取模数组容量,计算哈希地址
  • 哈希冲突:将产生冲突的节点挂载到同一链表中
  • 相较于闭散列:需要额外的指针域空间,但是不会影响别的节点查找和插入,整体插入、查找效率较高

哈希节点(链表):

// 哈希节点(单链表)
template <class T>
struct HashNode
{
	T _value;
	HashNode<T>* _next;

	HashNode(const T& value = T())
		:_value(value)
		, _next(nullptr)
	{}
};
  • 哈希函数:除留余数法:key % _table.size()
  • 解决哈希冲突:将冲突元素挂载到同一链表中
  • 扩容时机:当负载因子大于 1 时,扩容至 2 倍,并将原始数组中的节点插入新数组
    • 负载因子:有效元素个数 / 数组容量
  • 扩容方法:
    • 创建新的数组,并将旧表中的哈希节点插入到新表中,没有必要重新创建节点
    • 交换两个变量,因此扩容函数结束时会释放临时变量
    • 或者提供素数表:每次都扩容至素数大小,原因:素数只能被 1 和 本身整除,因此造成哈希冲突的可能性就降低了
  • 删除一个节点:
    • 通过哈希函数计算哈希地址,然后完成单链表删除工作
#include <iostream>
#include <vector>
using namespace std;


template <class K>
struct KofV
{
	const K& operator()(const K& key)
	{
		return key;
	}
};

// 哈希表
template <class K, class V, class KofV>
class HashTable
{
	typedef HashNode<V> Node;
public:
	HashTable(size_t size = 5)
		:_table(size)
		, _size(0)
	{}
	bool Insert(const V& value)
	{
		// 检查容量
		checkCapacity();
		// 计算位置
		KofV kov;
		int index = kov(value) % _table.size();

		// 查重
		Node* cur = _table[index];
		while (cur)
		{
			if (kov(cur->_value) == value)
				return false;
			cur = cur->_next;
		}

		// 进行头插
		cur = new Node(value);
		
		cur->_next = _table[index];
		_table[index] = cur;

		++_size;
		return true;
	}

	void checkCapacity()
	{
		// 负载因子 = 有效元素个数 / 数组容量 = 1时需要扩容
		if (_size == _table.size())
		{
			size_t newSize = 2 * _size;
			vector<Node*> newHt(newSize);
			
			KofV kov;
			// 遍历原数组,将所有节点插入新数组
			for (size_t i = 0; i < _table.size(); ++i)
			{
				Node* cur = _table[i];
				while (cur)
				{
					// 保存旧表下一个元素
					Node* next = cur->_next;

					// 重新计算新表位置,并将其插入新表
					int index = kov(cur->_value) % newHt.size();
					cur->_next = newHt[index];
					newHt[index] = cur;

					cur = next;
				}
				_table[i] = nullptr;
			}
			_table.swap(newHt);
		}
	}

	Node* find(const K& key)
	{
		if (_table.size() == 0)
			return nullptr;
		
		int index = key % _table.size();
		Node* cur = _table[index];
		KofV kov;
		while (cur)
		{
			if (kov(cur->_value) == key)
				return cur;
			cur = cur->_next;
		}
		return nullptr;
	}

	bool erase(const K& key)
	{
		int index = key % _table.size();

		Node* cur = _table[index];
		Node* prev = nullptr;
		KofV kov;
		while (cur)
		{
			if (kov(cur->_value) == key)
			{
				if (prev == nullptr)
					_table[index] = cur->_next;
				else
				{
					prev->_next = cur->_next;
				}
				delete cur;
				--_size;
				return true;
			}
			prev = cur;
			cur = cur->_next;
		}
		return false;
	}
private:
	vector<Node*> _table;
	size_t _size;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值