【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;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是一种常见的数据结构,可以快速地进行查找、插入和删除操作。哈的核心思想是将数据元素通过哈函数映射到一个固定大小的数组中,每个数据元素在数组中对应一个位置,通过查询这个位置可以快速地访问该元素。 线性探测再是一种解决哈冲突的方法。当两个不同的数据元素映射到同一个数组位置时,线性探测再会尝试在数组中找到另一个空位置,并将冲突的数据元素插入到该空位置中。如果没有找到空位置,线性探测再会重新计算哈函数,并尝试将数据元素插入到新的位置中。 下面是一个基于C语言实现的线性探测再的代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define HASH_TABLE_SIZE 100 typedef struct _hash_node { char *key; char *value; } hash_node; typedef struct _hash_table { hash_node **nodes; int size; } hash_table; unsigned long hash_function(char *key) { unsigned long hash = 5381; int c; while ((c = *key++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash; } hash_table *create_hash_table(int size) { hash_table *table = (hash_table *) malloc(sizeof(hash_table)); table->nodes = (hash_node **) calloc(size, sizeof(hash_node *)); table->size = size; return table; } void destroy_hash_table(hash_table *table) { int i; for (i = 0; i < table->size; i++) { if (table->nodes[i]) { free(table->nodes[i]->key); free(table->nodes[i]->value); free(table->nodes[i]); } } free(table->nodes); free(table); } void insert_hash_table(hash_table *table, char *key, char *value) { unsigned long hash = hash_function(key) % table->size; int i = 0; while (table->nodes[hash] && strcmp(table->nodes[hash]->key, key) != 0) { i++; hash = (hash + i) % table->size; } if (table->nodes[hash]) { free(table->nodes[hash]->value); table->nodes[hash]->value = strdup(value); } else { table->nodes[hash] = (hash_node *) malloc(sizeof(hash_node)); table->nodes[hash]->key = strdup(key); table->nodes[hash]->value = strdup(value); } } char *lookup_hash_table(hash_table *table, char *key) { unsigned long hash = hash_function(key) % table->size; int i = 0; while (table->nodes[hash] && strcmp(table->nodes[hash]->key, key) != 0) { i++; hash = (hash + i) % table->size; } if (table->nodes[hash]) { return table->nodes[hash]->value; } else { return NULL; } } int main() { hash_table *table = create_hash_table(HASH_TABLE_SIZE); insert_hash_table(table, "foo", "bar"); insert_hash_table(table, "baz", "qux"); printf("%s\n", lookup_hash_table(table, "foo")); /* bar */ printf("%s\n", lookup_hash_table(table, "baz")); /* qux */ printf("%s\n", lookup_hash_table(table, "quux")); /* (null) */ destroy_hash_table(table); return 0; } ``` 在该代码中,我们定义了一个`hash_node`结构体示哈中的一个节点,其中包含了一个键值对。我们还定义了一个`hash_table`结构体示哈本身,其中包含了一个节点数组和哈的大小。 我们使用`hash_function`函数来计算键的哈值,并使用`create_hash_table`函数来创建一个指定大小的哈。`destroy_hash_table`函数用于销毁哈。 我们使用`insert_hash_table`函数来向哈中插入一个键值对,该函数先计算键的哈值,然后在哈中查找是否已经存在该键,如果存在则更新其值,否则创建一个新的节点。当出现哈冲突时,我们使用线性探测再来寻找下一个空位置。 我们使用`lookup_hash_table`函数来查找哈中指定键的值,该函数先计算键的哈值,然后在哈中查找该键。 最后,在`main`函数中,我们创建了一个哈,向其中插入两个键值对,并且使用`lookup_hash_table`函数查找指定键的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值