当前位置: 首页 > news >正文

哈希--哈希桶

哈希桶是哈希表(散列表)中的一个概念,是哈希表数组中的每个元素 ,用于存储键值对数据。它有以下特点和相关要点:

  • 结构与原理:哈希表底层常由数组构成,数组的每个元素即哈希桶。通过哈希函数计算键的哈希值,该值作为索引指向对应的哈希桶。比如,用哈希函数Hash(key)=key % 10计算键key的哈希值,就能确定其对应的哈希桶位置。
  • 冲突处理:不同键经哈希函数计算后可能得到相同哈希值,即哈希冲突。常见的处理方式有:
    • 链地址法:也叫开散列法,每个哈希桶维护一个链表(或其他可链接的数据结构)。冲突发生时,将新键值对添加到对应哈希桶的链表末尾。例如,多个键计算出的哈希值都对应数组的第 3 个位置,这些键值对就依次链接在该位置的链表上。
    • 开放寻址法:不借助额外链表,所有键值对直接存于哈希表数组。冲突发生时,通过线性探测、二次探测或双重哈希等方式,在数组中找下一个空闲位置存储键值对。
  • 负载因子与扩容:负载因子 = 填入表中元素的个数 / 散列表的长度,用于衡量哈希表的填充程度。负载因子过高会增加哈希冲突概率,影响性能。当达到预设阈值(如 0.75)时,通常会对哈希表进行扩容,创建更大容量的数组,并重新计算所有键的哈希值,将键值对迁移到新数组的哈希桶中 。
  • 应用场景:广泛应用于需要快速查找、插入和删除数据的场景,如数据库索引、缓存系统、编程语言中的集合类(如 Java 的HashMap、C++ 的unordered_map) 。像在电商系统中,可使用哈希桶快速查找商品信息;在编译器中,用于符号表的管理,快速查找变量和函数定义。

哈希桶的实现:

namespace hash_bucket
{
	template<class K>
	struct hashFunc
	{
		size_t operator()(const K& key)
		{
			return static_cast<size_t>(key);
		}
	};
	template<>
	struct hashFunc<string>
	{
		size_t operator()(const string& key)
		{
			size_t hash = 0;
			for (auto e : key)
			{
				hash *= 131;
				hash += e;
			}
			return hash;
		}
	};



	template<class K,class V>
	struct HashNode
	{
		HashNode<K, V>* next;
		pair<K, V> _kv;
		HashNode() {};
		HashNode(const pair<K,V>& kv):_kv(kv),next(nullptr){}
	};

	template<class K,class V,class Hash=hashFunc<K>>
	class hashTables
	{
		using node = HashNode<K, V>;
		//typedef HashNode<K, V> node;
	public:
		hashTables()
		{
			_tables.resize(10);
		}

		bool insert(const pair<K, V>& kv)
		{
			if (find(kv.first))
			{
				return false;
			}
			//下面是扩容,这里需要注意的是它的负载因子可以达到 1 
			/*if (_n == _tables.size())
			{
				size_t newsize = _tables.size() * 2;
				hashTables<K, V> newTables;
				newTables._tables.resize(newsize);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					node* cur = _tables[i];
					while (cur)
					{
						newTables.insert(cur->_kv);
						cur = cur->next;
					}
				}
				_tables.swap(newTables._tables);
			}*/
			//这一段相比较上一段减少了insert调用,减少了开销!
			if (_n == _tables.size())
			{
				vector<node*> newtables;
				newtables.resize(_tables.size() * 2);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					node* cur = _tables[i];
					while (cur)
					{
						node* next = cur->next;

						size_t hashi = hs(cur->_kv.first) % newtables.size();
						cur->next = newtables[hashi];
						newtables[hashi] = cur;

						cur = next;

					}
					_tables[i] = nullptr;
				}
				_tables.swap(newtables);
			}

			size_t hashi = hs(kv.first) % _tables.size();
			node* newnode = new node(kv);
			//头插
			newnode->next = _tables[hashi];
			_tables[hashi] = newnode;//更新头结点!
			++_n;
		}

		node* find(const K& key)
		{
			size_t hashi =hs(key) % _tables.size();
			node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					return cur;
				}
				cur = cur->next;
			}
			return nullptr;
		}
		bool erase(const K& key)
		{
			size_t hashi =hs( key) % _tables.size();
			node* cur = _tables[hashi];
			node* prev = nullptr;
			while (cur)
			{
				/*prev = cur;*/
				
				if (cur->_kv.first == key)
				{
					if (prev==nullptr)//头删
					{
						_tables[hashi] = cur->next;
					}
					else//后续正常删
					{
						prev->next = cur->next;
					}
					delete cur;
					return true;
				}
				prev = cur;
				cur = cur->next;
			}
			return false;

		}
		~hashTables()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				node* cur = _tables[i];
				while (cur)
				{
					node* next = cur->next;
					delete cur;
					cur = next;
				}

				_tables[i] = nullptr;
			}
		}
		void print()
		{
			for (size_t i = 0; i <_tables.size(); i++)
			{
				node* cur = _tables[i];
				while (cur)
				{
					cout << "key: " << cur->_kv.first << "-> " << cur->_kv.second << endl;
					cur = cur->next;
				}
			}
		}
	private:
		vector<node*> _tables;//指针数组
		size_t _n=0;
		Hash hs;
	};
}
void test_hush3()
{
	hash_bucket::hashTables<int, int> h;
	int a[] = { 1,2,3,4,5,6,7 };
	for (auto e : a)
	{
		h.insert(make_pair(e, e));

	}
	h.insert({ 8,8 });
	h.print();
	h.erase(8);
	h.print();
}

头插的时候:

上面的代码要注意的是它的扩容,它直接重新申请一个数组链表,而不是像开放地址法一样,进行一个重新搞一个对象,然后在对象的新表里面进行挪动数据了。现在哈希桶的这个扩容方法减少了insert的调用!!!

相关文章:

  • wordpress主题开发框架(灵狐框架),开发文档使用教程
  • Docker 搭建 PlantUML 服务:高效生成 UML 图的最佳实践
  • 算法-动态规划二
  • Day38 | 1365. 有多少小于当前数字的数字、941. 有效的山脉数组、1207. 独一无二的出现次数、283. 移动零、189. 轮转数组
  • shell脚本一键安装docker+docker-compose,支持x86_64、arm64双架构
  • 桑福德·韦尔策划美国捷运公司收购南美银行案例分析
  • 数组子序列比较的SIMD优化
  • 字典树与01trie
  • 数值分析作业插值法2
  • CD18.【C++ Dev】类和对象(9)(声明和定义分离的写法以及代码复用)
  • php webshell免杀
  • Hive问题记录(1)
  • SingleMod
  • inline 配置全局参数变量
  • 深入解析 Spring Framework 5.1.8.RELEASE 的源码目录结构
  • 驱动编写-DS18B20温度传感器
  • 远场分量(平面波角谱)与倏逝波
  • 搜索旋转排序数组
  • 初见MyBatis
  • 区间端点(java)(贪心问题————区间问题)
  • 金华网站建设网站/市场调研的基本流程
  • 万网域名优惠口令/临沂seo排名外包
  • 电子商务网站建设/seo收录查询工具
  • 手表价格网站/表白网站制作
  • 网页制作与网站建设初学者必看教程/如何拿高权重网站外链进行互换?
  • 天津品牌网站建设好处/武汉seo关键字优化