【C++】哈希一

news2024/11/22 18:23:57

这篇博客要说的是哈希算法哈希又称为散列,它是将存储的和存储的位置建立起关联关系的一种算法,或者说是一种任意长度的数据映射为固定长度的输出的算法

什么意思呢?我们来看一个例子:比如说我们要存储1,2,3,4,5,6这几个数据,我们可以开6个空间大小的数组用于存储,正好下标(位置)跟数据(值)之间是有一定的关系的,很容易存储。

但是假如是下面6个数据呢?1,2,3,1000,1001,1002难道我们还要开1000个空间不成?当然不可以,这太浪费了,于是就开始想办法,让它们都对于总个数6取余不就得到范围比较小的值了吗。它们取余后分别得到1,2,3,4,5,0,我们让这几个数据当作它们各自的位置,这样每一个数据都和一个位置相互对应了,这就是一种解决问题的方法。

上面两个例子已经很形象的说明了什么是哈希,并且第一个例子就是我们的直接定址法,第二个例子就是除留余数法

但是我们的除留余数法还是有问题的,有没有可能两个数取余后得到的数相同?肯定是有可能的,这种情况就叫哈希冲突。我们可以知道,哈希冲突越多,那么效率就越低。所以我们一般当负载因子或者叫载荷因子就是实际存的数据个数除以表的大小)大于某个数就要扩容,增大表的大小。这样就可以一定程度的保证效率。那么接下来我们就要解决哈希冲突,有两种方法,一种叫闭散列开放定址法,一种叫开散列拉链法,也叫哈希桶

它们分别是什么意思呢?下面我们分别来说,闭散列开放定址法就是在这个固定长度的数组中如果一个值要放的位置已经有其他的值了,那么就从这个位置向后边找,直到找到空的位置放入,如果找到结尾,那么再返回头去找。这个向后边找可以一个一个找,就叫线性探测,也可以1,4,9,16.....这样二次方数这样找,就叫做二次探测

下面一个是哈希桶也叫开散列拉链法,就是我们在哈希表中存某个数据所在节点的指针,如果下个数据仍然在这个位置,那么就挂在上个数据的下边就可以了,挂上数据之后就像一个桶或者像拉链,于是名字由此得名

那么接下来我们就分别用这两种解决哈希冲突的方法来实现一下哈希表,这里我们的哈希表中的值先按整形看,等后边我们再慢慢加上模板等一系列东西,先看第一种方法

enum state {
	EMPTY,
	EXIST,
	DELETE
};
struct HashNode {
	int val=0;
	state state=EMPTY;
};
class HashTable {
public:
	HashTable(size_t n = 10) {
		_hashvec.resize(n);
	}
	HashNode* find(size_t key) {
		int hashi = key % _hashvec.size();
		while (_hashvec[hashi].state != EMPTY && _hashvec[hashi].val != key) {
			hashi++;
			hashi %= _hashvec.size();
		}
		if (_hashvec[hashi].state == EMPTY)return nullptr;
		return &_hashvec[hashi];
	}

	bool insert(size_t data) {
		if (find(data))return false;
		if (_n * 10 / _hashvec.size() >= 7) {
			//扩容
			HashTable newtable;
			newtable._hashvec.resize(_hashvec.size()*2);
			for (size_t i = 0; i < _hashvec.size(); i++) {
				if(_hashvec[i].state==EXIST)
				newtable.insert(_hashvec[i].val);
			}
			_hashvec.swap(newtable._hashvec);
		}
		size_t hashi = data % _hashvec.size();
		while (_hashvec[hashi].state == EXIST) {
			hashi++;
			hashi %= _hashvec.size();
		}
		_hashvec[hashi].val = data;
		_hashvec[hashi].state = EXIST;
		_n++;
		return true;
	}
	bool erase(size_t data) {
		HashNode* tmp = find(data);
		if (tmp == nullptr)return false;
		tmp->state = DELETE;
		--_n;
		return true;
	}
private:
	size_t _n=0;
	vector<HashNode> _hashvec;
};

再看第二种方法

struct HashNode {
		HashNode(size_t n=0)
			:val(n)
			,_next(nullptr){}

		size_t val = 0;
		HashNode* _next = nullptr;
	};
	class HashTable {
	public:
		HashTable(size_t n=10) {
			_hashvec.resize(n, nullptr);
		}
		HashNode* find(size_t key) {
			size_t hashi = key % _hashvec.size();
			HashNode* cur = _hashvec[hashi];
			while (cur) {
				if (cur->val == key)return cur;
				cur = cur->_next;
			}
			return nullptr;
		}
		bool insert(size_t key) {
			if (find(key))return false;
			if (_n == _hashvec.size()) {
				//扩容
				HashTable newtable(_hashvec.size() * 2);
				for (size_t i = 0; i < _hashvec.size(); i++) {
					HashNode* cur = _hashvec[i];
					HashNode* next = nullptr;
					while (cur) {
						next = cur->_next;
						size_t hashi = cur->val % newtable._hashvec.size();
						cur->_next = newtable._hashvec[hashi];
						newtable._hashvec[hashi] = cur;

						/*if (newtable._hashvec[hashi] == nullptr) {
							newtable._hashvec[hashi] = cur;
						}
						else {
							HashNode* tmp = newtable._hashvec[hashi];
							while (tmp->_next) {
								tmp = tmp->_next;
							}
							tmp->_next = cur;
						}
						cur->_next = nullptr;*/
						cur = next;
					}
					_hashvec[i] = nullptr;
				}
				_hashvec.swap(newtable._hashvec);
			}
			size_t hashi = key % _hashvec.size();
			HashNode* newnode = new HashNode(key);
			newnode->_next = _hashvec[hashi];
			_hashvec[hashi] = newnode;
			++_n;
			return true;
		}
		bool erase(size_t key) {
			size_t hashi = key % _hashvec.size();
			HashNode* prev = nullptr;
			HashNode* cur = _hashvec[hashi];
			while (cur&&cur->val != key) {
				prev = cur;
				cur = cur->_next;
			}
			if (cur == nullptr)return false;
			if (prev == nullptr) {
				_hashvec[hashi] = cur->_next;
			}
			else {
				prev->_next = cur->_next;
			}
			delete cur;
			return true;
		}
	private:
		size_t _n = 0;
		vector<HashNode*> _hashvec;
	};
}

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

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

相关文章

[RK3399 Linux] 使用busybox 1.36.1制作rootfs

一、 编译、安装、配置 busybox 1.1 下载源码 根文件系统是根据busybox来制作的。 下载地址:https://busybox.net/downloads/。 这里就以1.36.1版本为例进行编译安装介绍: 注意:编译linux内核与文件系统中的所有程序要使用相同的交叉编译器。 下载完成后解压: mkdir …

三大能力升级!大模型开启智能客服新篇章

当前智能化已成为各行各业加速转型发展的关键词&#xff0c;客户服务领域也不例外&#xff0c;将大语言模型与文档问答结合&#xff0c;不仅能够有效提升知识构建效率&#xff0c;重塑智能客服模式&#xff0c;还将成为企业营销、运营智能化进程中的重要助推力&#xff01; 接…

C语言基础:回顾判断素数

什么是素数&#xff08;也称质数&#xff09;&#xff1f;和合数相对。 其特点是只能被 1 和它本身 整除&#xff0c;无法被其他整数整除。或者公因数只有它自己和1两个数的数 怎么求解素数呢&#xff1f;对于求解质数的方法很多&#xff0c;但是有一种专门求解素数的功能&am…

Pytest接口自动化测试进阶

背景 随着Web应用的发展&#xff0c;越来越多的功能需要用户登录才能使用。而在接口测试中&#xff0c;往往需要模拟用户的登录状态来进行测试。一种常见的做法是通过Cookie来维持用户的登录状态。然而&#xff0c;由于Cookie的有效期限制以及网站的安全策略&#xff0c;如何在…

leetcode每日一题第四十六天

递归解法 class Solution { public:int search(vector<int>& nums, int target) {return midsearch(nums,target,0,nums.size()-1);}int midsearch(vector<int>& nums, int target, int low,int high){if(low < high){int mid (lowhigh) / 2;if(nums[…

别让这6个UI设计雷区毁了你的APP!

一款成功的APP不仅仅取决于其功能性&#xff0c;更取决于用户体验&#xff0c;这其中&#xff0c;UI设计又至关重要。优秀的UI设计能够为用户带来直观、愉悦的交互体验&#xff0c;甚至让用户“一见钟情”&#xff0c;从而大大提高产品吸引力。 然而&#xff0c;有很多设计师在…

.NET Framework安装失败的原因及解决方法

.NET Framework安装失败的原因及解决方法 大家好我是艾西&#xff0c;一个做服务器租用的游戏爱好者兼网络架构系统环境问题网络工具人。在我们平时使用PC安装某些程序会出现.NET Framework缺失的提示&#xff0c;那么也会有很多的小伙伴搞不懂什么原因导致的&#xff0c;这个问…

【C++】unordered 系列关联式容器

文章目录 1. unordered 系列关联式容器2. unordered_map2.1 unordered_map 的文档介绍2.2 unordered_map 的接口说明 3. unordered_set4. 在线 OJ 1. unordered 系列关联式容器 在 C 98 中&#xff0c;STL 提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可…

C/C++进阶/架构师(后端/音视频/游戏/嵌入式/高性能网络/存储/基础架构/安全)开发学习路线、系统性学习教程

C学习可以划分为几个主要阶段&#xff0c;每个阶段的学习目标和推荐资源都有所不同。下面是一个详细的分阶段学习指南&#xff1a; 入门阶段 学习目标 理解C的基本语法和结构。学习基本数据类型&#xff0c;条件判断&#xff0c;循环等控制结构。掌握函数的使用方法。初步了…

MongoDB副本集部署(windows)

环境准备 本教程演示mongodb4.4 副本集部署&#xff08;一主两从&#xff0c;伪分布式&#xff09; 节点配置主节点localhost:27017从节点1localhost:27018从节点2localhost:27019 每一个节点&#xff08;实例&#xff09;都创建对应的数据文件&#xff08;data&#xff09;…

从零自制docker-9-【管道实现run进程和init进程传参】

文章目录 命令行中输入参数长度过长匿名管道从父进程到子进程传参[]*os.File{}os.NewFile和io.ReadAllexe.LookPathsyscall.Execstrings.Split(msgStr, " ")/bin/ls: cannot access : No such file or directory代码 命令行中输入参数长度过长 用户输入参数过长或包…

acwing2060. 奶牛选美

题目&#xff1a; 代码&#xff1a; //acwing2060. 奶牛选美 #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int N55; const int dx[]{-1,0,1,0},dy[]{0,-1,0,1}; bool st[N][N]; int point[N][N]; char map[N][…

微信小程序中调取小程序实现报错:提示 开发版小程序已过期,请在开发者工具中重新扫码的 解决方案

出现的问题&#xff1a; 解决方法&#xff1a; 将envVersion: develop,开发版切换为正式版 envVersion: release,wx.navigateToMiniProgram({appId:res.data.appId,path: res.data.prePayTn,extraData: {foo: bar,miniProgramOrgId:res.data.miniProgramOrgId,orderId: res.d…

游标的定义和类型

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 游标的基本概念 游标从字面上理解为游动的光标&#xff0c;可以使用 Excel 表格来想象游标的作用&#xff0c;游标指向每一行&#xff0c;通过游标访问每行数据。 在 Orac…

前端图片详解(最全面、最新)

前言 当我们在做前端性能优化的时候&#xff0c;总是会离不开图片&#xff0c;尤其在首次内容绘制&#xff08;FCP&#xff09;和最大内容绘制 (LCP)中&#xff0c;图片显得格外关键&#xff0c;而我发现关于图片格式的文章&#xff0c;一般不全&#xff0c;或者是偏旧。 所以…

STC89C52学习笔记(十)

STC89C52学习笔记&#xff08;十&#xff09; 综述&#xff1a;本文介绍了DS18B20和单总线协议&#xff0c;以及讲述了如何使用DS18B20测量温度。 一、单总线协议 1.只有一根通讯线&#xff1a;DQ &#xff08;常见的运用单总线的两种设备&#xff1a;DS18B20和DHT11&#…

sectigo ov企业通配符证书

OV通配符SSL证书是Sectigo旗下比较受欢迎的一款数字证书。Sectigo成立时间较长&#xff0c;旗下的数字证书产品可以使用RSA或者ECC等加密算法保护网站传输信息安全&#xff0c;Sectigo旗下的数字证书可以兼容大多数主流浏览器。今天就随SSL盾小编了解Sectigo旗下的OV企业通配符…

10BASE-T1S架构助力车载E/E领域,引领汽车产业迈向智能化新纪元!

汽车架构的发展 如今&#xff0c;汽车已不仅仅满足消费者的代步需求&#xff0c;而是向所谓的ACES&#xff08;Autonomous, Connected, Electrification, Shared Source&#xff09;方向发展&#xff0c;全自动驾驶和网联化将成为最终目标。由此带来的高算力和高数据吞吐量问题…

AR智能眼镜方案_MTK平台安卓主板芯片|光学解决方案

AR眼镜作为一种引人注目的创新产品&#xff0c;其芯片、显示屏和光学方案是决定整机成本和性能的关键因素。在这篇文章中&#xff0c;我们将探讨AR眼镜的关键技术&#xff0c;并介绍一种高性能的AR眼镜方案&#xff0c;旨在为用户带来卓越的体验。 AR眼镜的芯片选型至关重要。一…

tkinter窗口

简单的窗口程序 导入所需的库 from tkinter import * import json 创建一个主窗口 app Tk() 设置窗口大小为 1048x2048 app.geometry(“1048x2048”) 设置窗口背景为灰色 app.configure(bg“gray”) 创建一个 Label 对象&#xff0c;显示 “账号&#xff1a;” 和红色…