【C++】 哈希 (上)

news2024/11/18 9:48:48

文章目录

    • 1. 哈希概念
    • 2. 哈希表
      • 直接定址法(常用)
      • 除留余数法(常用)
      • 解决哈希冲突方法1 ——闭散列
    • 3. 闭散列的实现
      • 如何处理删除数据?
      • 定义数据结构
      • insert
        • len为 _tables.size() 还是 _tables.capacity()?
        • 线性探测
        • 负载因子
        • 扩容
      • Find
      • Erase
      • 完整代码

1. 哈希概念

理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。
如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立
一一映射的关系,那么在查找时通过该函数可以很快找到该元素。
当向该结构中:
插入元素
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
搜索元素
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置
取元素比较,若关键码相等,则搜索成功
该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称
为哈希表(Hash Table)(或者称散列表)

2. 哈希表

key跟存储位置建立映射(关联)关系

直接定址法(常用)

每一个值都有一个唯一位置
特点:适用于范围比较集中的数据

除留余数法(常用)

特点:范围不集中,分布分散

当前数据非常分散,虽然最大值已经达到1000,但是空间使用效率太低,所以不应该开1000个空间储存
所以想要把分散的数据,映射到固定的空间中


key值跟存储位置的关系,是模出来的
不同的值有可能映射到相同的位置 即哈希冲突
如55与15取模后的值都为5

解决哈希冲突方法1 ——闭散列

闭散列又称 开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明哈希表中必然还有空位置,则可以把key存放到冲突位置中的下一个位置去


如何寻找下一个位置?
线性探测

若有两个取模相同的值,则将先进来的占住当前取模位置,后进来的向后探测,若有空位置则放入

因为是先将2取模,所以2占住了映射2的位置,而当将102取模时,由于位置被2占住,所以向后寻找空位置,即在映射4的位置


hashi=key%len
len代表 表的长度
若当前位置已经被占住,hashi+i (i>=0)
i从0开始,不断增加直到找到一个没有占住的位置,超过该表的长度

3. 闭散列的实现

当使用除留余数法解决问题时
不同的值映射在相同的位置,即哈希冲突/哈希碰撞


使用线性探测处理,依次找后面位置存储 hashi + i (1,2,3,4)

如何处理删除数据?

假设要删除33,因为33取余后为3,所以先去位置为3的地方去找,没有找到,则继续向后寻找
寻找到空才结束


在这里插入图片描述
假设把33直接删除,当再次查找13时,由于提前遇到空,则直接结束
所以找到后,并不能直接删除,因为会影响查找
设置三种状态: 空、存在、删除

定义数据结构

需要使用枚举来表示三种状态 删除 存在 空

表示状态的state 也应该默认设为空,不然有可能造成 映射位置 没有数据但是 状态为存在的情况

insert

hashi=key%len
len代表 表的长度
若当前位置已经被占住,hashi+i (i>=0)


len为 _tables.size() 还是 _tables.capacity()?

假设将hashi的大小设为capacity
若当前位置为空,则将值填入进去,并且将状态设置为存在,会造成越界
在vector中 operator[] 会做越界检查,下标是否小于size


无法访问10后面的数据的,会造成越界


len为_tables.size()

线性探测


若从3位置开始,则+7时,绕过去导致达到0位置处,所以需要将index%size,从而达到0位置处


若当前位置存在,则继续向后走,若遇到空或者删除状态,则停下来填入数据,并将其设置为存在状态,存储的数据个数+1

负载因子

哈希表冲突越多,效率越低
若表中位置都满了,就需要扩容 ,提出负载因子的概念


负载因子 = 填入表的元素个数 / 表的长度
表示 表储存数量的百分比

填入表的元素个数 越大,表示冲突的可能性越大,
填入表的元素个数 越小,表示冲突的可能性越小
所以在开放定址法时,应该控制在0.7-0.8以下,超过就会扩容

扩容

需要注意的是 整形 除以整形 不存在小数


可以选择将其分子扩大10倍,则除出来的结果 为整数


表为空没有处理,无法扩容
size的大小没有变化,改变的caoacity的大小
但是增加的capacity的空间是不能被访问到的



size刚开始时为10,通过扩容size变为20
再次寻找13时,13%20 ==13 , 而13所在位置是4 ,所以是找不到的
说明当前扩容方法是不可以的


在这里插入图片描述
开辟一块新的空间,将原来空间内的数据都重新计算在新空间的映射位置
映射关系变了
原来冲突的值可能不冲突了
原来不冲突的值可能冲突了


创建newht,将其中的_tables的size进行扩容
通过 复用insert的方式,完成对新表的映射
交换旧表与newht的_tables ,以达到更新数据的目的


在这里插入图片描述

Find

若当前位置的状态为存在或者删除,则继续找, 遇见空 就结束
若在循环中找到了,则返回对应位置的地址,若没找到则返回nullptr


虽然把要删除的数据的状态改为DELETE,但是数据本身还是在的
所以Find还是可以找到的


所以只有当前位置的数据状态为EXIST时并且当前位置的数据等于key值,才返回

Erase

通过Find寻找要删除的数据,若找到了,则将其状态改为DELETE 将其数据个数减1 并返回true ,若没有找到,则返回false

完整代码

#pragma once

#include<vector>
#include<iostream>
using namespace std;
enum State //表示三种状态
{
	EMPTY, //空
	EXIST,//存在
	DELETE//删除

};

template<class K,class V>
struct HashData
{
	pair<K, V>_kv;//对应的KV 数据
	State _state=EMPTY;//表示状态
};

template<class K,class V>
class HashTable
{
public:
	bool insert(const pair<K, V>& kv) //插入
	{
		//若数据已经有了,就不要插入了
		if (Find(kv.first))
		{
			return false;
		}
		
		//负载因子超过0.7就扩容
		if (_tables.size()==0 || _n * 10 / _tables.size() >= 7)
		{
			size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;	
			
			HashTable<K, V> newht;
			newht._tables.resize(newsize); //将newht中的_tables 进行扩容
			//遍历旧表,重新映射到新表
			for (auto & data : _tables)
			{
				newht.insert(data._kv);//将旧表数据进行插入newht中
			}

			//将_tables 更新为newht中的数据
			_tables.swap(newht._tables);
		}

		 
		//key值%当前表的长度
		size_t hashi = kv.first % _tables.size();
		
		//线性探测
		size_t i = 1;
		size_t index = hashi;
		while (_tables[index]._state == EXIST)//若当前位置值存在,则继续往后走
		{
			//加i是因为有可能后面的位置被占用,则需要找到一个没有被占用的位置
			index = hashi + i;

			//为了针对绕过去的情况
			index %= _tables.size();
			i++;//i的值从1开始 依次递增
		}
		_tables[index]._kv = kv;//填入数据
		_tables[index]._state = EXIST;//设置为存在状态
		_n++;
		return true;
	}

	HashData<K, V>* Find(const K& key)//查找
	{

		if (_tables.size() == 0)
		{
			return false;
		}

		size_t hashi = key % _tables.size();

		//线性探测
		size_t i = 1;
		size_t index = hashi;
		while (_tables[index]._state != EMPTY)
		{
			if (_tables[index]._state==EXIST&&_tables[index]._kv.first == key)//找到
			{
				return   &_tables[index];
			}

			//加i是因为有可能后面的位置被占用,则需要找到一个没有被占用的位置
			index = hashi + i;

			index %= _tables.size();
			i++;//i的值从1开始 依次递增
			
			//表里全是删除/存在状态的数据
			if (index == hashi)
			{
				break;
			}
		}
		return nullptr;	
	}
	 
	bool Erase(const K& key)
	{
		HashData<K, V>* ret = Find(key);
		if (ret)
		{
			ret->_state == DELETE;
			--_n;
			return true;
		}
		else
		{
			return false;
		}
	}
	 
private:
	vector<HashData<K,V>> _tables;
	size_t _n = 0; // 存储的数据个数
		
}; 

void hashtest()
{
	int a[] = { 3,33,2,13,5,12,1002 };
	HashTable<int, int>v;
	for (auto e : a)
	{
		v.insert(make_pair(e, e));
	}
}

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

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

相关文章

SpringBoot项目打包部署到Nginx【无需配置Nginx】

0.前置知识 springboot打包的项目共分为jar和war两种类型 jar包 jar类型项目使用SpringBoot打包插件打包时&#xff0c;会在打成的jar中 内置一个tomcat 的jar 所以我们可以使用jdk直接运行&#xff0c;将功能代码放到其内置的tomcat中运行。 war包 在打包时需要将 内置的tom…

2023-05-20 技术与管理的照本宣科教条主义与从事实中成长-反思

摘要: 最近的两份工作遇到了极其严重的教条主义, 有多严重呢&#xff1f;几乎可以说人成了教条的教徒&#xff0c;做事就成了照本宣科的死板硬套, 简直匪夷所思。 结果就是对于现实问题简直就像是建立在空中楼阁之上&#xff0c;不但涉及到管理&#xff0c;更有技术上的。 教…

实训三:MLP配置(多链路捆绑的配置)

实训三&#xff1a;MLP配置 【实验目的】 掌握多链路捆绑的配置。验证配置。 【实验拓扑】 实验拓扑如图所示。 设备参数如表所示。 设备 接口 IP地址 子网掩码 默认网关 R1 Multilink 1 192.168.12.1 255.255.255.0 N/A R2 Multilink 1 192.168.12.2 255.255…

6年心得,从功能测试到测试开发,送给在测试路上一路走到黑的你

蓦然回首&#xff0c;软件测试风风雨雨的这就几年&#xff0c;起初每天的工作只是鼠标点点点&#xff0c;我还真不知道怎么办&#xff0c;闲的时候真的怀疑自己的存在价值&#xff0c;每天拿着7000的工资&#xff0c;飘荡在繁华的深圳&#xff0c;吃不饱也饿不死&#xff0c;未…

1.2 IAR 环境配置及编译

目录 一. 新建源码文件夹 二. 添加源文件到工程中 三. 编写一个简单的测试程序 四. 设置字体和行号 五. 工程配置 六. 编译链接工程 一. 新建源码文件夹 &#xff08;1&#xff09;在保存工作空间和工程的目录下&#xff0c;新建一个code文件夹&#xff0c;用于保存源码&…

斯坦福、Nautilus Chain等联合主办的 Hackathon 活动,现已接受报名

由 Stanford Blockchain Accelerator、Zebec Protocol、 Nautilus Chain、Rootz Lab 共同主办的黑客松活动&#xff0c;现已接受优秀项目提交参赛申请。 在加密行业发展早期&#xff0c;密码极客们就始终在对区块链世界基础设施&#xff0c;在发展方向的无限可能性进行探索。而…

计算机组成原理实验三-修改二进制文件的程序改变最终运行结果

实验资料&#xff1a; https://wwpv.lanzoue.com/b05drr8qh 密码:6wjx 计算机组成原理实验三 修改二进制文件的程序改变最终运行结果 复习实验二GCC选项&#xff1a;-E -S -c -o -v .\gcc.exe 1.c -> a.exe .\gcc.exe 1.c -o 21001302xx.exe&#xff08;1&#xff09;…

Nacos之服务配置中心

1.基础配置 1.1.新建模块cloudalibaba-config-nacos-client3377 1.1.1.POM <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance…

JVM学习(五)

1.1 JVM 类加载机制 JVM 类加载机制分为五个部分&#xff1a;加载&#xff0c;验证&#xff0c;准备&#xff0c;解析&#xff0c;初始化&#xff0c;下面我们就分别来看一下这 五个过程。 1.1.1 加载 加载是类加载过程中的一个阶段&#xff0c; 这个阶段会在内存中生成一…

路径规划算法:基于教与学优化算法的路径规划算法- 附代码

路径规划算法&#xff1a;基于教与学优化优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于教与学优化优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能…

Springcloud1--->负载均衡Ribbon

目录 负载均衡算法负载均衡原理启动两个服务实例开启负载均衡更改Ribbon随机策略 什么是Ribbon&#xff1a; 负载均衡算法 负载均衡算法&#xff1a; 1.轮询法&#xff1a;   将请求按顺序轮流地分配到后端服务器上&#xff0c;它均衡地对待后端的每一台服务器&#xff0c;而…

【正点原子STM32连载】 第十三章 跑马灯实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第十三…

chatgpt赋能Python-python3_9_7怎么用

介绍Python3.9.7及其用途 Python是一种高级编程语言&#xff0c;已成为Web开发、数据科学、机器学习等领域中最广泛使用的语言之一。Python3.9.7是Python的最新版本&#xff0c;于2021年9月6日发布。它包括各种新的特性、改进和安全性实现&#xff0c;提高了Python应用程序的稳…

计算机图形学-GAMES101-13

Ray Tracing &#xff08;1&#xff09;为什么使用光线追踪 Ray Tracing 和 Rasterization 是两种不同的成像方式。Rasterization最大的问题&#xff1a;不利于表达全局效果。整体来说光栅化做阴影是比较困难的。Glossy reflection&#xff1a;一种不那么光滑的反射镜面。Ind…

在 Linux 上使用 yuzu 模拟 Nintendo Switch 试玩王国之泪

王国之泪5月12日发售&#xff0c;DLC 玩家已经造出各种脑洞大开的东西了&#xff0c;但是买的卡带迟迟没有收到&#xff0c;因此&#xff0c;打算使用 yuzu 模拟器先体验一下 yuzu 是一款开源的 Ninetendo Switch 模拟器&#xff0c;支持在 Linux 或者 Windows 平台运行&#…

GoLand 2023 Crack函数的支持

GoLand 2023 Crack函数的支持 增加了对“MIN_BY”和“MAX_BY”函数的支持。 更新了Prisma插件previewFeatures以包含jsonProtocol。 改进了与角度相关的符号的文档-添加了更多关于管道、特性和指令的文档。当您将鼠标悬停在符号上或调用显示文档完成时(F1/CtrlQ)&#xff0c;您…

linux工作目录切换命令文件查看及管理命令

1、查看用户工作目录 2、切换工作目录 这里使用cd命令即可&#xff0c;输入对应的路径就可切换。 如果要返回上一次所处的目录输入 cd - 如果要返回上层目录使用 cd … 返回用户家目录使用 cd ~ 3、查看目录中文件信息命令 使用ls命令可以查看目录中文件信息-a 参数可以…

Windows 和 Linux 环境下 ProtoBuf 的安装

文章目录 一、ProtoBuf 在 Windows 环境中的安装二、ProtoBuf 在 Linux 环境中的安装 ProtoBuf在GitHub上的下载地址 一、ProtoBuf 在 Windows 环境中的安装 首先选择自己要下载的版本&#xff0c;我选择的是v21.11&#xff1a; 点进去在最下面选择Windows的版本&#xff0…

New:dbForge Edge 2023 4in1 Enterprise Edition Crack

dbForge Edge 2023 4in1 Enterprise Edition 赋予自己开发和管理 SQL Server、MySQL、Oracle 和 PostgreSQL 数据库的广泛能力 dbForge Edge&#xff1a;您的终极多数据库解决方案 让我们来看看。您需要处理多个数据库管理系统。同时&#xff0c;您希望能够灵活有效地处理范围广…