数据结构: 哈希桶

目录

1.概念

2.模拟实现

2.1框架

2.2哈希桶结构

2.3相关功能

Modify

--Insert

--Erase

--Find

2.4非整型数据入哈希桶

1.仿函数

2.BKDR哈希


1.概念

具有相同地址的key值归于同一集合中,这个集合称为一个桶,各个桶的元素通过单链表链接

数据结构: 哈希桶_第1张图片

2.模拟实现

2.1框架

a.写出哈希桶的结构: hash_node  + hash_table     节点  +   指针数组

b.思路: 增删查改的实现 + 扩容  

c.使用除留余数法获得在哈希表中的位置~~>将非整型数据转换为整型数据~~>提供仿函数

2.2哈希桶结构

哈希桶本质是一个指针数组,里面存放的是指针,指向一个链表~~>使用vector来存放  +  n(负载因子)

namespace HashBucket 
{
	//1.节点
	template
	struct HashNode 
	{
		HashNode* _next;
		T _data;
		//构造函数
		HashNode(const T& data)
			:_next(nullptr)
			,_data(data)
		{}
	};

	//2.哈希表
	template
	class HashTable 
	{
		typedef HashNode Node;
	public:
		//接口
	private:
		vector* _tables;
		size_t n = 0; //有效数据个数
	};
}

2.3相关功能

Modify

--Insert

思路:

1.判断是否需要扩容

        a.扩容~~>将原来的数据移动到新开辟的空间

        b.不扩容~~>直接头插

2.头插

        a.计算这个值在哈希表中的位置

        b.单链表的头插

        c.更新hash_table

--将旧数据移动到新开辟的空间

方式1:复用insert~~>额外开辟新节点 + 释放旧的空间

数据结构: 哈希桶_第2张图片

方式2:直接挪动

        a.开辟新空间(创建新表)

        b.遍历原链表~~>取节点下来头插(保存下一个节点 + 重新计算hashi + 头插)

        c.交换新旧表

数据结构: 哈希桶_第3张图片

        

--单链表的头插:

数据结构: 哈希桶_第4张图片

代码:

		bool Insert(const T& data) 
		{
			//1.扩容检查
			if (n == _tables.size()) 
			{
				size_t newsize = _tables.size() == 0 ? 10 : 2 & _tables.size();
				vector newtables(newsize, nullptr);	//创建新表
				//将原来的数据挪动到新表
				for (auto& cur : _tables) 
				{
					while (cur) 
					{
						//保存下一个节点
						Node* next = cur->_next;
						//重新计算在哈希桶中的位置
						size_t hashi = (cur->_data) % newtables.size();
						//放进新桶
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
				}
				//交换
				_tables.swap(newtables);
			}

			//2.头插数据
			Node* newnode = new Node(data);      //创建节点
			size_t hashi = data % _tables.size();//计算在在个哈希桶
			newnode->_next = _tables[hashi];	 
			_tables[hashi] = newnode;
			++n;

			return true;
		}

效果:

数据结构: 哈希桶_第5张图片

           

--Erase

思路:

1.计算data在哪个哈希桶

2.删除节点

        a.头删~~>更新头

        b.非头删除~~>找pre链接next

数据结构: 哈希桶_第6张图片

代码:

		bool Erase(const T& data) 
		{
			//1.计算在哪个哈希桶
			size_t hashi = data % _tables.size();
			//2.删除节点
			Node* pre = nullptr, * cur = _tables[hashi];
			while (cur) 
			{
				//记录下一个节点
				Node* next = cur->_next;
				if (cur->_data == data) 
				{
					//头删
					if (pre == nullptr) 
					{
						delete cur; cur = nullptr;
						_tables[hashi] = next;
						return true;
					}
					//非头删
					else 
					{
						pre->_next = next;
						delete cur; cur = nullptr;
						return true;
					}
				}
				//没找到继续往后找
				pre = cur;
				cur = cur->_next;
			}
			return false;
		}

效果

--非头删:

数据结构: 哈希桶_第7张图片--头删

数据结构: 哈希桶_第8张图片

--Find

思路:

如果哈希表没有元素,直接返回

1.计算在哪个哈希桶

2.遍历这个哈希桶,看有没有这个值

代码:

		Node* Find(const T& data) 
		{
			//如果这个哈希表为空, 直接返回
			if (_tables.size() == 0)return nullptr;

			//1.计算在哪个哈希桶
			size_t hashi = data % _tables.size();
			//2.遍历这个桶,找data
			Node* cur = _tables[hashi];
			while (cur) 
			{
				if (cur->_data == data)
					return cur;
				cur = cur->_next;
			}
			//遍历完都没有找到
			return nullptr;
		}

效果:

数据结构: 哈希桶_第9张图片

2.4非整型数据入哈希桶

由于哈希桶是采用除留余数法 ~~>  算在哪个哈希桶 ~~> 必须是整型数据

1.仿函数

使用仿函数将非整型数据转换为整型

代码:

	//仿函数
	template
	struct HashFunc 
	{
		size_t operator()(const T& data) 
		{
			return data;
		}
	};

2.BKDR哈希

若数据类型是string, "abc" 与"cba"通过相同的哈希函数计算出来的hashi,会相同

~~>引入BKDR哈希, 每次算一个字符的时候, hash *= 31 ~~>让计算出来的hashi尽可能不同

代码:

	//模板特化
	template<>
	struct HashFunc 
	{
		size_t operator()(const string& s) 
		{
			size_t hash = 0;
			for (auto ch : s) 
			{
				hash += ch;
				hash *= 31;
			}
			return hash;
		}
	};

效果:

数据结构: 哈希桶_第10张图片

你可能感兴趣的:(数据结构,C/C++/Linux实例,数据结构,哈希算法,算法)