【数据结构 09】哈希

news2024/12/23 10:06:42

哈希算法:哈希也叫散列、映射,将任意长度的输入通过散列运算转化为固定长度的输出,该输出就是哈希值(散列值)。

哈希映射是一种压缩映射,通常情况下,散列值的空间远小于输入值的空间。

哈希运算的结果称为哈希值,哈希运算是不可逆过程,即不能通过哈希值推算出原值。

哈希运算常用于加密、位图、布隆过滤,位图的作用是海量数据的标记,布隆过滤器的作用是提高海量数据查询的效率(客户端向服务端查询数据)。

一、哈希函数

HashFunc.h

#pragma once
#include <iostream>

// 仿函数
template<class K>
struct HashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

// 特化
template<>
struct HashFunc<std::string>
{
	size_t operator()(const std::string& str)
	{
		size_t res = 0;
		for (const auto& ch : str)
		{
			res *= 131;	// 随机数取值,避免哈希冲突
			res += ch;
		}
		return res;
	}
};

哈希表:将数据根据哈希运算得到哈希值(关键值),再根据哈希值将数据映射在表中,哈希表通常情况是一个vector容器。哈希表分为闭散列和开散列(哈希桶)。

哈希表的数据增删与红黑树差别不大,各有优劣,但是哈希表的数据查询效率远高于红黑树。

二、闭散列

#define _CRT_SECURE_NO_WARNINGS 1

#pragma
#include <iostream>
#include <vector>
#include "HashFunc.h"

enum status
{
	EMPTY,
	EXIST,
	DELETE
};

template<class K, class V>
struct CloseHashNode
{
	std::pair<K, V> _kv;
	status _status = EMPTY;
};

template<class K, class V, class Hash = HashFunc<K>>
class CloseHash
{
	typedef CloseHashNode<K, V> Data;
public:
	CloseHash()
		: _n(0)
	{
		_table.resize(10);
	}

	bool Insert(const std::pair<K, V>& kv)
	{
		if (Find(kv.first))
			return false;

		// 负载因子为0.7
		if (_n * 10 / _table.size() >= 7)
		{
			std::vector<Data> newTable;
			newTable.resize(2 * _table.size());
			for (int i = 0; i < _table.size(); ++i)
			{
				if (_table[i]._status == EXIST)
				{
					size_t pos = Hash()(_table[i]._kv.first) % newTable.size();
					while (newTable[pos]._status != EMPTY)
					{
						pos = (++pos) % newTable.size();
					}
					newTable[pos] = _table[i];
				}
			}
			_table.swap(newTable);
		}

		size_t pos = Hash()(kv.first) % _table.size();
		while (_table[pos]._status != EMPTY)
		{
			pos = (++pos) % _table.size();
		}
		_table[pos]._kv = kv;
		_table[pos]._status = EXIST;
		++_n;
		return true;
	}

	Data* Find(const K& key)
	{
		size_t pos = Hash()(key) % _table.size();
		int cnt = 0;
		while (_table[pos]._status != EMPTY && cnt != _table.size())
		{
			if (key == _table[pos]._kv.first && _table[pos]._status == EXIST)
				return &_table[pos];
			pos = (++pos) % _table.size();
			++cnt;
		}
		return nullptr;
	}

	bool Erase(const K& key)
	{
		Data* ret = Find(key);
		if (ret)
		{
			ret->_status = DELETE;
			--_n;
			return true;
		}
		else
		{
			return false;
		}
	}

private:
	std::vector<Data> _table;
	size_t _n;
};

三、开散列

开散列也称哈希桶,哈希桶的vector节点存储的是数据节点,相同哈希值的节点以链表的形式存储在同一个vector位置上,当节点数与vector容量的比值为平衡因子值(1)时,哈希桶扩容,扩容时重新遍历原表,将原表的元素重新取哈希进行映射,为了提高效率,不拷贝节点,而是改变节点的指向。

#define _CRT_SECURE_NO_WARNINGS 1

#pragma once
#include <iostream>
#include <vector>
#include "HashFunc.h"

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

template<class K, class V, class Hash = HashFunc<K>>
class OpenHash
{
	typedef OpenHashNode<K, V> Node;
public:
	OpenHash()
		: _n(0)
	{
		_table.resize(10, nullptr);
	}

	bool Insert(const std::pair<K, V>& kv)
	{
		if (Find(kv.first))
			return false;

		// 检查扩容,平衡因子为 1
		if (_n == _table.size())
		{
			std::vector<Node*> newTable;
			newTable.resize(2 * _table.size(), nullptr);

			for (int i = 0; i < _table.size(); ++i)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->next;

					size_t pos = Hash()(cur->kv.first) % newTable.size();
					cur->next = newTable[pos];
					newTable[pos] = cur;

					cur = next;
				}
			}

			_table.swap(newTable);
		}

		// 插入新节点
		Node* newNode = new Node(kv);
		size_t pos = Hash()(newNode->kv.first) % _table.size();
		newNode->next = _table[pos];
		_table[pos] = newNode;
		++_n;
		return true;

	}

	Node* Find(const K& key)
	{
		size_t pos = Hash()(key) % _table.size();
		Node* cur = _table[pos];
		while (cur)
		{
			if (cur->kv.first == key)
				return cur;
			cur = cur->next;
		}
		return nullptr;
	}

	bool Erase(const K& key)
	{
		Node* ret = Find(key);
		if (ret)
		{
			size_t pos = Hash()(key) % _table.size();
			Node* cur = _table[pos];
			if (cur == ret)
			{
				cur = ret->next;
				delete ret;
				ret = nullptr;
			}
			else
			{
				while (cur->next != ret)
				{
					cur = cur->next;
				}
				cur->next = ret->next;
				delete ret;
				ret = nullptr;
			}
			--_n;
			return true;
		}
		else
		{
			return false;
		}
	}

private:
	std::vector<Node*> _table;
	int _n;
};

四、测试

#define _CRT_SECURE_NO_WARNINGS 1

#include "CloseHash.h"
#include "OpenHash.h"
using namespace std;

void TestCloseHash()
{
	cout << "CloseHash: " << endl << endl;
	CloseHash<int, int> hash;

	int arr[] = { 34, 36, 12, 54, 5, 22, 65, 32, 13, 4, 1, 52 };
	for (auto& e : arr)
	{
		hash.Insert(make_pair(e, e));
	}

	cout << hash.Find(12) << endl;
	cout << hash.Find(22) << endl;
	cout << hash.Find(32) << endl;
	cout << hash.Find(42) << endl;
	cout << hash.Find(52) << endl;

	cout << endl;
	hash.Erase(32);
	cout << hash.Find(12) << endl;
	cout << hash.Find(22) << endl;
	cout << hash.Find(32) << endl;
	cout << hash.Find(42) << endl;
	cout << hash.Find(52) << endl;
}

void TestOpenHash()
{
	cout << endl << endl << "OpenHash: " << endl << endl;
	OpenHash<int, int> hash;

	int arr[] = { 34, 36, 12, 54, 5, 22, 65, 32, 13, 4, 1, 52 };
	for (auto& e : arr)
	{
		hash.Insert(make_pair(e, e));
	}

	cout << hash.Find(12) << endl;
	cout << hash.Find(22) << endl;
	cout << hash.Find(32) << endl;
	cout << hash.Find(42) << endl;
	cout << hash.Find(52) << endl;

	cout << endl;
	hash.Erase(32);
	cout << hash.Find(12) << endl;
	cout << hash.Find(22) << endl;
	cout << hash.Find(32) << endl;
	cout << hash.Find(42) << endl;
	cout << hash.Find(52) << endl;
}

int main()
{
	TestCloseHash();
	TestOpenHash();

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1431934.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis核心技术与实战【学习笔记】 - 22.浅谈Redis的ACID相关知识

概述 事务是数据库的一个重要功能。所谓的事务&#xff0c;就是指对数据进行读写的一系列操作。事务在执行时&#xff0c;会提供专门的属性保证&#xff0c;包括原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isol…

python脚本将照片按时间线整理

说明&#xff1a;有一次自己瞎折腾&#xff0c;然后把服务器相册搞崩了&#xff0c;后来做了备份同步给找了回来&#xff0c;但是相册的时间线全乱了&#xff0c;看起来非常难受。所以就想通过文件夹的形式把照片重新分类&#xff0c;分类后的结构如下(红色字体为文件夹)&#…

西瓜书学习笔记——核化线性降维(公式推导+举例应用)

文章目录 算法介绍实验分析 算法介绍 核化线性降维是一种使用核方法&#xff08;Kernel Methods&#xff09;来进行降维的技术。在传统的线性降维方法中&#xff0c;例如主成分分析&#xff08;PCA&#xff09;和线性判别分析&#xff08;LDA&#xff09;&#xff0c;数据被映…

React实例之完善布局菜单(三)

接着上篇的内容继续。规划界面布局。界面分为三个部分&#xff0c;左边为菜单部分&#xff0c;右边上部有个 80 px 高度左右的功能区&#xff0c;下面是主内容区。 依据这个设计&#xff0c;我们进行下面的步骤&#xff1a; 在 SMenu项目中创建一个目录&#xff1a; SLayout, …

解放网工双手-SNMP如何做好运维辅助?

1. SNMP为什么被誉为“网管神器”&#xff1f; 2. SNMP不同版本有何区别&#xff1f; 3. SNMP有哪些问题及Telemetry有何优势&#xff1f; ---- SNMP ----- 简单网络管理协议 U2000&#xff1a;传输设备管理 企业&#xff0c;银行 esight&#xff1a;华为 iMaster NCE-Camp…

【webpack】技巧使用

webpack和TypeScript 安装webpack相关内容安装TS相关内容配置初始化数据初始化运行展示和目录展示报错解决&#xff08;缺失文件配置&#xff09; 安装前端必备神奇lodash测试一下entry配置index.html模板配置修改打包出来的index.html的titleinject注入chunks 属性多页面配置 …

从 20 多套 MySQL 到 1 套 TiDB丨骏伯网络综合运营管理平台应用实践

原文来源&#xff1a; https://tidb.net/blog/a38c72a4 本文作者&#xff1a;骏伯网络 唐帆&#xff0c;PingCAP 贺美存 骏伯网络简介 广州骏伯网络是一家以数据驱动的科技公司&#xff0c;聚焦移动互联网营销服务&#xff0c;坚持以客户为中心&#xff0c;深耕 APP、运营…

大数据学习之Redis,十大数据类型的具体应用(四)

3.8 Redis基数统计&#xff08;HyperLogLog&#xff09; 需求 统计某个网站的UV、统计某个文章的UV 什么是UV unique Visitor &#xff0c;独立访客&#xff0c;一般理解为客户端IP 大规模的防止作弊&#xff0c;需要去重复统计独立访客 比如IP同样就认为是同一个客户 需要去…

sqli.labs靶场(29到40关)

29、第二十九关 id1 id1 尝试发现是单引号闭合&#xff0c; -1 union select 1,2,3-- -1 union select 1,2,database()-- -1 union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schemasecurity)-- -1 union select 1,2,(select…

国内最全的Spring Boot系列之七

• 阿里巴巴前高级研发工程师 • 三家千万级互联网企业技术顾问 • MBTI/盖洛普技术专家 • 厦门某高校外聘教师 • 51CTO特约合作讲师 • 网易云课堂签约讲师 •《深入理解设计模式》作者 一转眼马上要过年了&#xff0c;回首2023年&#xff0c;感觉自己无所事事、碌碌无…

python爬虫5

1.selenium交互 无页面浏览器速度更快 #配置好的自己不用管 from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionschrome_options Options()chrome_options.add_argument(‐‐headless)chrome_options.add_argument(‐‐disable‐gpu)# path…

编程效率的跃升之路

编程是一门需要大量的时间和精力投入的技能&#xff0c;提高编程效率则是一个需要不断学习和实践的过程。结合笔者写bug多年的经验&#xff0c;一些学习建议和资源和大家share下。 一、编程如何提效&#xff1a; 熟悉开发工具&#xff1a;掌握常用的开发工具&#xff0c;如集…

flutter抓包绕过

lutter的证书校验 起因&#xff1a; 最近工作上让做个app的复测&#xff0c;把apk发我后&#xff0c;开始尝试挂代理抓包&#xff0c;结果发现抓不到 为是证书没弄好&#xff0c;想着前几天不是刚导入了吗&#xff08;雾&#xff09;。又重新导入了下还是不行。然后各种lsp模…

OJ_找位置

题干 代码 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<vector> #include<algorithm> #include<map> using namespace std;int main() {char str[200] { 0 };scanf("%s", str);map<char, vector<int>> times…

ROS入门之通信机制及常用API

文章目录 前言一、话题通信1.话题通信理论2.话题通信常用API&#x1f357;发布者advertisepublish &#x1f356;订阅者subscribe 3.自定义msg 二、服务通信1.服务通信理论2.服务通信常用API&#x1f386;服务端advertiseService &#x1f387;客户端serviceClientros::service…

[大厂实践] Pinterest通用计算平台实践

Pinterest平台团队开发实现了名为PinCompute的高性能通用计算平台&#xff0c;支持Pinterest的大量异构用例和服务。本文介绍了团队在开发这一平台过程中的经验和实践&#xff0c;对于其他平台团队来说&#xff0c;具有很好的参考意义。原文: PinCompute: A Kubernetes Backed …

RabbitMQ面试必备:基本概念、组件原理、消息传递模型,一网打尽。解密高可用性、负载均衡,深入了解安全性配置和性能优化

一、RabbitMQ的基本概念&#xff1a; 1.什么是消息队列&#xff1f; 消息队列是一种在分布式系统中用于在不同组件之间传递消息的通信机制。它允许应用程序和服务通过异步方式进行通信&#xff0c;提高了系统的可伸缩性和松耦合性。消息队列通常包括生产者&#xff08;Produc…

node-sass版本与NodeJS版本不匹配的问题

npm install 报错如下 npm ERR! code 1 npm ERR! path D:\Project\git_Product\YYYY\user\node_modules\node-sass npm ERR! command failed npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c node scripts/build.js 问题原因 node-sass 与 node 版本不匹配 卸载Node…

PM圆桌派:同事不愿意告诉你的职场套路有哪些?

职场是社会的缩影&#xff0c;想要崭露头角&#xff0c;获得更多升职加薪的机会&#xff0c;就不要做着和多数人一样的事情&#xff0c;却期待着不一样的结果。 职场上有很多潜在的规则&#xff0c;要会做事&#xff0c;也要会说话&#xff0c;更要会做人。如果不懂规则&#…

day43_jdbc

今日内容 0 复习昨日 1 SQL注入问题 2 PreparedStatement 3 完成CRUD练习 4 ORM 5 DBUtil (properties) 6 事务操作 0 复习昨日 已经找人提问… 1 SQL注入 1.1 什么是SQL注入 用户输入的数据中有SQL关键词,导致在执行SQL语句时出现一些不正常的情况.这就是SQL注入! 出现SQL注入…