unordered系列关联式容器底层哈希结构的介绍,哈希表的模拟实现(哈希冲突的解决方法采用闭散列线性探测)

目录

前言

unordered系列关联式容器之所以处理数据的效率比较高,是因为底层使用了哈希结构,哈希结构的优点是:不经过任何比较,一次直接从表中得到要搜索的元素,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

一、哈希的概念

顺序结构以及平衡树 中,元素关键码与其存储位置之间没有对应的关系,因此在 查找一个元素
时,必须要经过关键码的多次比较 搜索的效率取决于搜索过程中元素的比较次数。
理想的搜索方法: 可以 不经过任何比较,一次直接从表中得到要搜索的元素 如果构造一种存储结构,通过某种函数 (hashFunc) 使元素的存储位置与它的关键码之间能够建立 一一映射的关系,那么在查找时通过该函数可以很快找到该元素
向结构中插入元素:根据待插入元素的关键码,用hashFunc函数(通常是用除留余数法)算出该元素的存储位置并按此位置进行存放。
从结构中搜索元素:对元素的关键码进行同样的计算,把求得的函数值当作元素的存储位置,在结构中按此位置取元素比较,若关键码相等,责搜索成功。
该方法即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称
为哈希表(HashTable)或者叫作散列表。
例如:数据集合{176459}
哈希函数设置为: hash(key) = key % capacity ;(capacity为存储元素底层空间总的大小。)
用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快。 但是如果按照上述哈希方
式,向集合中插入元素55,就会发生哈希冲突。

二、哈希冲突以及解决冲突的方法(闭散列线性探测)

不同关键码通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞
通常把具有不同关键码而具有相同哈希地址的数据元素称为 同义词
那么该如何解决这种冲突呢?
闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有
空位置,那么可以把 key 存放到冲突位置中的 下一个 空位置中去。(“下一个”不一定只是隔了一个位置,因为闭散列中有两种方法:线性探测和二次探测。线性探测则是下一个,二次探测则不一定是下一个位置)。
下面我们介绍一下解决哈希冲突的一个方法闭散列线性探测。
线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止
插入数据:
通过哈希函数获取待插入元素在哈希表中的位置。
如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突, 使用线性探测找到 下一个空位置,插入新元素 。
删除元素:
采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素
会影响其他元素的搜索 。比如删除元素5 ,如果直接删除掉, 55查找起来可能会受影 响。因此 线性探测采用标记的伪删除法来删除一个元素

三、哈希表的模拟实现(哈希冲突的解决方法采用闭散列线性探测)

#include<vector>
#include<iostream>
using namespace std;namespace keke
{enum Status{EMPTY,EXIST,DELETE};template<class K,class V>struct HashData{pair<K, V> _kv;Status _s;};//仿函数/*template<class K>class HashFunc{public:size_t operator()(const K& key){size_t i = 0;for (auto e : key){i += e;}return i % _tables.size();}};*/template<class K, class V>class HashTable{public:HashTable(){_tables.resize(10);}bool Insert(const pair<K, V>& kv){//负载因子:已经存储数据个数/_tables大小if (Find(kv.first))return false;//扩容if ((double)_n / _tables.size() >= 0.7){size_t newSize = _tables.size() * 2;HashTable<K, V> newHashTable;//定义一个新的哈希表,为什么不将原来的哈希表扩容呢?这是//因为如果扩容的话就会影响查找数据,比如:当原哈希表的空间大小为5在下标为3的位置插入关键码3时再插入13//就要根据线性探测的方法将关键码13存入到关键码3的下一个位置处,但是如果原地2倍扩容后,在进行Find时关键码3正常//但是当Find关键码13时就会出现问题newHashTable._tables.resize(newSize);for (size_t i = 0;i < _tables.size();++i)//遍历旧的哈希表{if (EXIST == _tables[i]._s){newHashTable.Insert(_tables[i]._kv.first);}}//将临时创建的扩容后的哈希表里的_tables与旧哈希表里的_tables互换_tables.swap(newHashTable._tables);}size_t hashi = kv.first % _tables.size();while (_tables[hashi]._s != EXIST){hashi++;hashi %= _tables.size();//当hashi的值等于_tables.size()的值则将hashi置为0}_tables[hashi]._kv = kv;_tables[hashi]._s = EXIST;++_n;return true;}HashTable<K, V>* Find(const K& key){size_t hashi = key % _tables.size();while (_tables[hashi]._s != EMPTY){if (_tables[hashi]._s == EXIST && _tables[hashi]._kv.first == key){return &_tables[hashi];}hashi++;hashi %= _tables.size();}return nullptr;}bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (ret){ret->_s = DELETE;--_n;return true;}return false;}void Print(){for (size_t i = 0;i < _tables.size();++i){if (EXIST == _tables[i]._s)printf("[&d]->%d\n", i, _tables[i]._kv.first);else if (EMPTY == _tables[i]._s)printf("[&d]->E\n", i);elseprintf("[&d]->D\n", i);}}private:vector<HashData<K, V>> _tables;size_t _n = 0;};
}
//范围小的向范围大的提升,有符号的向无符号的的提升

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

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

相关文章

mapreduce | 自定义Partition分区(案例2)

1.需求 统计每个手机号消费总金额&#xff0c;按照消费金额降序排序&#xff0c;最终联通、电信、移动分别写入不同的文件。 130、131、132&#xff08;联通&#xff09; 133&#xff08;电信&#xff09; 135、136、137、138、139 &#xff08;移动&#xff09; 手机号,消费记…

sql-行转列2(转置)

行转列的常规做法是&#xff0c;group bysum(if())【或count(if())】 例题&#xff1a; 表table如下&#xff1a; DDateshengfu2015-05-09胜2015-05-09胜2015-05-09负2015-05-09负2015-05-10胜2015-05-10负2015-05-10负 如果要生成下列结果, 该如何写sql语句? DDate胜负20…

【计算机网络】-性能指标(速率、带宽和吞吐量)

速率、带宽和吞吐量 信道&#xff08;channel&#xff09;&#xff1a; 表示向某个方向传输信息的通道&#xff0c;一条通信线路在逻辑上往往对应着一条发送信道和接收线道 速率&#xff08;speed&#xff09;&#xff1a; 指的是连接到网络上的节点在信道上传输的速率。也…

全局变量在函数中的使用:Python 编程技巧解析

在Python编程中&#xff0c;全局变量是一种在程序的多个部分中共享数据的机制。全局变量在函数中使用时&#xff0c;需要特别注意其作用域和访问方式。本文将详细讲解如何在函数中使用全局变量&#xff0c;并提供示例代码&#xff0c;帮助初学者深入理解这一概念。 基本原理 …

web3.js的使用

前端开发web3一共常用的库有4个&#xff0c;分别是&#xff1a; web3.js [核心库]ethereumjs-tx1.3.7 (这个包已经被弃用&#xff0c;只有1.3.7可用&#xff0c;如果采用ts开发则可以使用另一个包ethereumjs/tx) bip39 [助记词库]ethereumjs-wallet [钱包库] 注意&#xff1a…

AI作画涉及的深度学习算法

AI作画中使用的深度学习算法多种多样&#xff0c;这些算法主要基于神经网络的结构和训练方式&#xff0c;以生成和改进艺术作品。以下是一些在AI作画中常用的深度学习算法&#xff1a; 生成对抗网络&#xff08;GANs, Generative Adversarial Networks&#xff09;&#xff1a…

Linux —— 线程

Linux —— 线程 什么是线程Linux如何实现线程Winodws如何实现线程使用一下线程pthread_create函数原型参数说明返回值 如何解决 ps -aL 查看线程线程为什么轻量 我们今天进入线程的学习&#xff1a; 什么是线程 我们先来了解一个笼统的概念&#xff1a;简单来说&#xff0c;…

负载均衡技术

负载均衡技术 1. MapReduce MapReduce 是一种编程模型&#xff0c;旨在处理大规模数据集的并行计算任务&#xff0c;通常针对大于 1TB 的数据。该模型借鉴了函数式编程语言的概念&#xff0c;如 “Map” 和 “Reduce”&#xff0c;以及矢量编程语言的特性&#xff0c;使得编程…

计算机发展史故事【12】

芯片计算机 众所周知&#xff0c;所谓286、386、486 个人电脑等名称的起源&#xff0c;在于它们采用了英特尔公司研制的微处理器X86 系列芯片286、386 和486。然而&#xff0c;这种以数字为电脑命名的奇特现象&#xff0c;却来源于霍夫博士等人发明的世界上第一个微处理器芯片…

MySQL变量定义与使用

1 set userName小可爱; set userName:玛卡巴卡; select userName as名称; 2 set x5,y7,dx3.1425,dy6.678; selectxy as 四则运算; selectx-y as 四则运算; selectx*y as 四则运算; selectx/y as 四则运算; selectx%y as 四则运算; 3 set result1dxdy as 四则运算; set res…

动态路由-链路状态路由协议ospf案例

实验拓扑和要求如图 ospf实验 1.设置各个接口地址 2.测试ar5到ar6的连通性 3.配置ospf协议&#xff0c;routerid&#xff0c;area&#xff0c; 详细的网络信息&#xff0c;等待网络收敛后&#xff0c; 查看ospf信息&#xff0c;路由表信息&#xff0c;再次测试连通性 注意区域…

防火墙技术基础篇:网络地址转换(NAT):防火墙技术的核心机制

防火墙技术基础篇&#xff1a;网络地址转换&#xff08;NAT&#xff09;&#xff1a;防火墙技术的核心机制 网络地址转换&#xff08;NAT&#xff09;是现代网络架构中不可或缺的一个组成部分&#xff0c;尤其在防火墙技术的实现中扮演着重要角色。本文旨在全面解读NAT的工作机…

AI算法-高数5.1-线性代数-向量定义、表示和向量间的关系

看线性代数这篇文章&#xff08;AI算法-高数5-线性代数1-基本概念、向量-CSDN博客&#xff09;理解有些吃力的朋友们&#xff0c;可以先学下宋浩老师的这些课程。 宋浩老师&#xff1a; 3.1 n维向量及其运算_哔哩哔哩_bilibili 3.2 向量间的线性关系&#xff08;一&#xff…

【Python】深度学习中随机数种子seed的种类和设置方式

在机器学习和深度学习的实验中&#xff0c;确保实验的可重复性是非常重要的。下面定义的set_random_seed(seed) 函数的主要目的是设置随机种子以确保代码的随机性操作&#xff08;如参数初始化、数据集分割、随机数据增强等&#xff09;在不同运行之间是可重复的。这使得其他研…

MFC编程之设计美丽的对话框

目录 写在前面&#xff1a; Part 1&#xff1a;美美的设计一下计算器的布局 1.描述文字&#xff1a; ​编辑 2.ID&#xff1a; Part 2&#xff1a;美美熟悉一下计算器的工作流程 Part 3&#xff1a;美美设计一下控件功能 1.edit control&#xff1a; 2.相关变量初始化&…

go语言中io操作中的 io.Reader 和 io.Writer的获取方法 总结

我们在对文件进行io操作的时候&#xff0c;经常看到需要我们传递一个 io.Reader 或者 io.Writer 对象作为读写的入参&#xff0c; 那么我们该如何或者这些个RW对象呢&#xff1f; 其实很简单&#xff0c;你只需要查找一下哪些对象实现了 Read或者 Writer方法&#xff0c;那么你…

ajax的请求解密

$.ajax({url: i,dataType: "json",success: function(e) {r.decompressAndDeobfuscateData(e, w).then((e>{if (!(e JSON.parse(e))[0].series[0].serie.data[0])return t(!0);e r.transformData(e),I && console.log("result:", e);解释每一…

uniapp 百度地图 拖动获取经纬度级搜索连用

import loadBMap from /utils/loadBMap.js// 百度聚合具体代码 // 拖动 initMapc() {let that thisloadBMap(百度key).then(() > {map new BMap.Map(mapContainer)const centerPoint new BMap.Point(this.longitude, this.latitude)map.centerAndZoom(centerPoint, this.…

Agilent MSO9404A、Keysight MSO9404A示波器,4 GHz,4 通道,20 GSa/s

Agilent MSO9404A、Keysight MSO9404A、HP MSO9404A 示波器&#xff0c;4 GHz&#xff0c;4 通道&#xff0c;20 GSa/s Keysight MSO9404A 示波器配备 15 英寸 XGA 显示屏&#xff0c;封装深度仅为 9 英寸&#xff08;23 厘米&#xff09;&#xff0c;重量仅为 26 磅&#xff…

AI地名故事:笔岗村

笔岗村&#xff0c;实际上是由笔村和宏岗村两个古老的村落合并而成的。南宋度宗元年&#xff0c;也就是公元1265年&#xff0c;笔村开始建立。随着时间的推移&#xff0c;到了宋代后期&#xff0c;宏岗村也相继建立。这两个村落各自承载着丰富的历史和文化&#xff0c;最终在历…