STL容器之哈希

哈希

​ 哈希本质上还是一个数组,只是数组的每一个位置要存储的值进行了映射;

​ 哈希也可以叫做散列;

​ 哈希比红黑树快是因为,不需要重复进行比较大小,直接用映射关系进行查找;

​ 哈希函数的设计应该注意要减少哈希冲突;

1.unordered系列关联式容器的使用

​ Java设计的是HashMap与HashSet,TreeMap与TreeSet。

​ stl的map与set的底层是红黑树,unorderedmap与unorderedset的底层设计的是哈希表/桶;

​ 与map/set的区别是,1.迭代器使用的都是单向迭代器;2.遍历出来不是有序的,只能去重;3.哈希表的性能比起红黑树更高一些;

​ 注意插入重复数据时哈希更快,非重复时红黑树更快,其他如查找,删除都是哈希更快。

​ 对于有序数据还是红黑树更快,包括插入,查找,删除;

1.1unordered_set

在这里插入图片描述

在这里插入图片描述

1.2unordered_map

在这里插入图片描述

在这里插入图片描述

2.不同查找方式的对比

​ 1.暴力查找时间按复杂度O(N);

​ 2.有序数组的二分查找O(lgN),存在排序O(N*logN)和增删改需要挪动数据O(N)的问题。

​ 3.平衡搜索树O(lgN),天然有序,增删查改也比较方便;

​ 4.哈希(散列)存储的值和存储的位置建立一个对应关系,根据对应关系进行快速查找;

3.哈希

​ 1.计数排序使用的就是一种哈希;

​ 2.图书馆找书,根据字母排序进行哈希查找相关书籍;

​ 如果数据很分散,那就退化成了暴力查找;

3.1常见的哈希函数

​ 1.直接定址法,使用直接映射查找,一对一,适合数据集中使用,问题:数据分散就会退化成暴力查找;

​ 2.除留余数法,开辟capacity个位置的空间,int i = k % capacity,然后将key放到第i个位置,问题:会产生哈希冲突/碰撞,即不同的值可能会映射到同一个位置,值和位置的关系是多对一的关系;

​ 如果是整型直接进行映射,如果是对象或者字符串就需要先与整数进行映射,然后在用整数和位置进行映射;

3.2解决哈希冲突

​ 1.闭散列的开放定址法,缺陷是冲突会互相影响,如连续空间的值会互相影响;

​ 思路是:位置被占了,就找下一个位置(本质上找的是别人的位置),会造成踩踏拥堵;

	线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止,空间不够会进行扩容;

问题是:1.会不断的抢占位置;2.哈希表不能太满,否则会导致下降,所以是不可能找不到空位置的;3.删除会导致查找出错,所以需要存在三个状态标记,1.exist,2.empty,3.delete ;

​ 二次探测,与线性探测唯一的区别就是每次移动走的是平方,1 4 9 …,好处是每次查找的时候不是走连续的空间,若一段连续空间内存在多个值,就会降低哈希冲突的概率;

​ 2.开散列的拉链法(哈希桶):

​ 在原有顺序表的位置使用类似链表的方式,在顺序表外将多个值链接在此位置之后;

4.线性探测实现哈希表

​ 1.使用枚举变量,来控制状态;2.对于哈希数据要存放kv和状态枚举变量;3.对于哈希表使用vector来开辟空间存放哈希数据,使用size记录有效数据;4.设计负载因子,负载因子为表中的元素个数除以长度;

​ 对于插入函数的设计,1.要注意负载因子越大,产生哈希冲突的概率越大,但是空间利用率更高,所以综合一下控制负载因子到一定值就扩容,不能满了才扩容或空间利用率过低;2.扩容先计算新的空间大小,然后重新开辟一个哈希表,并且使用resize使得大小和空间相同,遍历旧表插入新表,然后交换新旧表;3.使用除留余数法来获取顺序表的下标;4…不断线性探测空位置;5.找到了空位置,插入哈希数据,设置kv并修改状态变量;

​ 对于删除函数的设计,较为简单先find,然后置为delete并且size–。

​ 由于映射使用的是下标位置是自然数,所以要注意对于非无符号整型要先建立无符号整型映射,推荐使用仿函数;对于常见的类型可以使用模板的特化;如果只是使用字符转整型相加还是右很多场景得到的值相同,难以避免哈希冲突,所以需要使用哈希算法BKDR算法,即每次加之前乘131,使用131是因冲突数相较于其他算法较少;

代码实现:

#pragma once#include <vector>namespace HashTable
{enum Status{EXIST = 0,EMPTY,DELETE,};template <class K, class V>struct HashData{std::pair<K, V> kv_;Status status_ = EMPTY;};template <class K>struct Default{size_t operator()(const K &key){return (size_t)key;}};template <>struct Default<std::string>{size_t operator()(const std::string &key){size_t i = 0;for (const auto &ch : key){i *= 131;i += ch;}return i;}};// struct stringfunc// {//     size_t operator()(const std::string& key)//     {// 		return key[0];//     }// };template <class K, class V, class HashFunc = Default<K>>class HashTable{public:HashTable(){table_.resize(10); // 使用resize保证size和capacity的值是一样的}bool insert(const std::pair<K, V> &kv) // 要注意不可以使得这个表太满,否则会降低查找效率,甚至表满了会不断死循环下去{if (find(kv.first)){return false;}HashFunc func;// 扩容逻辑,要注意的是扩容完成之后映射关系会发生改变,因为扩容后除数变大了if ((double)size_ / (double)table_.size() >= 0.7){size_t newsize = table_.size() * 2;HashTable<K, V, HashFunc> hashtable;hashtable.table_.resize(newsize);// 遍历旧表重新插入到新表for (const auto &e : table_){if (e.status_ == EXIST){hashtable.insert(e.kv_);}}// 交换新旧表table_.swap(hashtable.table_);}// 线性探测size_t hashi = func(kv.first) % table_.size(); // 不能%capacity因为断言检查的是size,会有越界风险,最好是使得size与capacity相等// 如果这个位置存在值,就需要探测到空停止if (func(table_[hashi].kv_.first) != func(kv.first)){while (table_[hashi].status_ == EXIST){hashi++;hashi %= table_.size();}// 找到了空位置进行插入table_[hashi].kv_ = kv;table_[hashi].status_ = EXIST;size_++;return true;}else{return false;}}HashData<const K, V> *find(const K &key){Default<K> func;size_t hashi = func(key) % table_.size();while (table_[hashi].status_ != EMPTY) // 由于delete状态可能是数据再后面位置,不能停止{if (table_[hashi].status_ != DELETE && table_[hashi].kv_.first == key){return (HashData<const K, V> *)&table_[hashi]; // 会产生隐式类型转化,有的编译器不支持需要显式转换}hashi++;hashi %= table_.size();}return nullptr;}bool erase(const K &key){HashData<const K, V> *pos = find(key);if (pos){pos->status_ = DELETE;size_--;return true;}return false;}private:std::vector<HashData<K, V>> table_; // 物理空间上就是一个是顺序表,逻辑上是一个哈希size_t size_ = 0;                   // 存放有效数据的个数};
}

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

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

相关文章

华为北向网管NCE开发教程(2)REST接口开发

华为北向网管NCE开发教程&#xff08;1&#xff09;闭坑选接口协议 华为北向网管NCE开发教程&#xff08;2&#xff09;REST接口开发 华为北向网管NCE开发教程&#xff08;3&#xff09;CORBA协议开发 假设你现在要开始华为北向接口REST协议之前&#xff0c;需要准备如环境 1准…

Vue中用户权限如何处理?

Vue中用户权限如何处理&#xff1f; 在 Vue 中&#xff0c;可以采用多种方式来处理用户权限&#xff0c;以下是一些常见的方法&#xff1a; 1. 使用路由守卫 Vue Router 提供了 beforeEach 导航守卫&#xff0c;可以在路由跳转之前进行权限检查。例如&#xff1a; router.be…

Leetcode3069. 将元素分配到两个数组中 I

Every day a Leetcode 题目来源&#xff1a;3069. 将元素分配到两个数组中 I 解法1&#xff1a;模拟 简单地按题意模拟。 代码&#xff1a; /** lc appleetcode.cn id3069 langcpp** [3069] 将元素分配到两个数组中 I*/// lc codestart class Solution { public:vector<…

【libwebrtc】基于m114

libwebrtc A C++ wrapper for binary release, mainly used for flutter-webrtc desktop (windows, linux, embedded).是 基于m114版本的webrtc 最新(20240309 ) 的是m122了。官方给出的构建过程 .gclient 文件 solutions = [{"name" : src,"url

微软AI工程师向联邦贸易委员会(FTC)发出警告,对Copilot Designer的安全性表示担忧

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

gitte上传项目操作

一、项目背景 打比赛&#xff0c;多个人合作&#xff0c;选择github&#xff0c;顺便了解下git的代码操作。 二、步骤 2.1 新建仓库 2.2 打开你要上传到库的项目 2.2 选择 Git Bash Here 输入指令 git init 2.3 查找github的仓库 2.2 将文件放入暂缓区 git add . 2.3填写…

LLM PreTraining from scratch -- 大模型从头开始预训练指北

最近做了一些大模型训练相关的训练相关的技术储备&#xff0c;在内部平台上完成了多机多卡的llm 预训练的尝试&#xff0c;具体的过程大致如下&#xff1a; 数据准备&#xff1a; 大语言模型的训练依赖于与之匹配的语料数据&#xff0c;在开源社区有一群人在自发的整理高质量的…

jeecgboot 新建子模块 使用@EXCEL实现实现导入导出功能

一&#xff0c;用框架生成增删改查模块 二&#xff0c;在实体类entity 需要导入导出的字段上加上注解Excel 三&#xff0c;在controller类上继承jeecgboot通用controller JeecgController 并且在JeecgController里增加导出模板的方法 /*** 导出excel空模板** param req…

专业140+总430+电子科技大学858信号与系统考研经验成电电子信息与通信工程,电科大,真题,大纲,参考书。

今年考研成绩出来&#xff0c;初试专业课858信号与系统140&#xff0c;总分430&#xff0c;其余各门分数都比较平稳&#xff0c;总分好于自己估分&#xff0c;应群里很多同学要求&#xff0c;我总结一下自己的复习经验。首先我是一个大冤种&#xff0c;专业课资料学长给了一套&…

挑战杯 基于深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

软考高级:系统工程生命周期方法(计划驱动方法、渐进迭代式方法等)概念和例子

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

如何在Ubuntu系统部署DbGate数据库管理工具并结合cpolar内网穿透远程访问

文章目录 1. 安装Docker2. 使用Docker拉取DbGate镜像3. 创建并启动DbGate容器4. 本地连接测试5. 公网远程访问本地DbGate容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 本文主要介绍如何在Linux Ubuntu系统中使用Docker部署DbGate数据库管理工…

web组态

演示地址 &#xff1a;by组态[web组态插件] 这是一款可以嵌入到任何项目组态插件&#xff0c;功能全面&#xff0c;可根据自己的项目需要进行二次开发&#xff0c;能大大的节省在组态上的开发时间&#xff0c;代码简单易懂。 一、数据流向图及嵌入原理 数据流向 嵌入原理 …

深度神经网络 基本知识 记录

资料&#xff1a;https://www.bilibili.com/video/BV1K94y1Z7wn/?spm_id_from333.337.search-card.all.click&vd_source14a476de9132ba6b2c3cbc2221750b99 计划&#xff1a;3~4天 注&#xff1a;网课讲的内容比较糅杂&#xff0c;记录的内容可能会出现重复 杂 人工智能…

<商务世界>《第8课 Leads——MQL——SQL——商机——成交》

1 各种概念 英文缩写概念Traffic流量Leads潜在客户&#xff0c;销售线索&#xff1b;简称潜在线索MQLMarketing-Qualified Leads市场认可线索SQLSales-Qualified Leads销售认可线索OPPOpportunity商机Account成单客户 2 线索到商机 一般企业会把自身线索进行如下的划分&…

【电工学笔记】上册第一、二章

电工学 上次考试败在了单位&#xff0c;这次单位 一定要记熟。 第一章 电源或信号源的电压或电流称为激励,它推动电路工作; 由激励所产生的电压和电流称为响应。 复杂电路中,一般无法事先判断某个支路电流的 实际方向或者某个电路元件电压的实际方向 140V/4算不出总电阻的 …

数据结构面试常见问题

数据结构面试常见问题 什么是 AVL 树&#xff1f;什么是红黑树&#xff1f;AVL 树和红黑树的区别&#xff1f;B 树和B 树的区别&#xff1f;排序有哪些分类&#xff1f;直接插入排序的原理&#xff1f;希尔排序的原理&#xff1f;直接选择排序的原理&#xff1f;堆排序的原理&a…

vue3的开发小技巧

「总之岁月漫长&#xff0c;然而值得等待。」 目录 父组件调用子组件函数如何访问全局api 父组件调用子组件函数 ref, defineExpose //父组件 代码 <child ref"ch">this.$refs.ch.fn();//子组件 函数抛出 const fn () > { }; defineExpose({ fn });如何…

考研复习C语言初阶(3)

目录 一.函数是什么? 二.C语言中函数的分类 2.1库函数 2.2自定义函数 三.函数的参数 3.1实际参数&#xff08;实参&#xff09; 3.2 形式参数&#xff08;形参&#xff09; 四.函数的调用 4.1 传值调用 4.2 传址调用 五. 函数的嵌套调用和链式访问 5.1 嵌套调用 5…

瑞芯微 | I2S-音频基础分享

1. 音频常用术语 名称含义ADC&#xff08;Analog to Digit Conversion&#xff09;模拟信号转换为数字信号AEC&#xff08;Acoustic Echo Cancellor&#xff09;回声消除AGC&#xff08;Automatic Gain Control&#xff09;自动增益补偿&#xff0c;调整MIC收音量ALSA&#xf…