【C++】哈希

1. unordered系列关联式容器

STL提供了底层为红黑树结构的一系列关联式容

这里介绍 unordered_set 和 unordered_map

a. unordered_map

  1. unordered_map 是存储<key, value>键值对的关联式容器,其允许通过 key 快速的索引到与

其对应的 value

  1. unordered_map 容器通过 key 访问单个元素要比 map 快,但它通常在遍历元素子集的范围迭

代方面效率较低

  1. unordered_map 实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value
  2. 它的迭代器至少是前向迭代器
  3. 重复数据可以去重

b. unordered_set

  1. unordered_set 允许通过 key 快速的索引是否存在
  2. 重复数据可以去重
  3. 删除,查找,插入,效率都很快

2. unordered_map 的接口说明

a. unordered_map 的容量

函数声明

1. bool empty() const

2. size_t size() const

功能介绍

1. 检测unordered_map是否为空

2. 获取unordered_map的有效元素个数

b. unordered_map 的迭代器

函数声明

1. iterator begin()

2. iterator end()

3. iterator cbegin() const

4. iterator cend() const

功能介绍

1. 返回 unordered_map 第一个元素的迭代器

2. 返回 unordered_map 最后一个元素下一个位置的迭代器

3. 返回 unordered_map 第一个元素的const迭代器

4. 返回 unordered_map 最后一个元素下一个位置的const迭代器

c. unordered_map 的元素访问

函数声明

1. K& operator[]

功能介绍

1. 返回与 key 对应的 value ,允许被修改

d. unordered_map 的查询

函数声明

1. iterator find(const K& key)

2. size_t count(const K& key)

功能介绍

1. 返回key在哈希桶中的位置

2. 返回哈希桶中关键码为key的键值对的个数

注意:

unordered_map 中 key 是不能重复的,因此 count函数的返回值最大为 1

e. unordered_map 的桶操作

函数声明

1. size_t bucket_count() const

2. size_t bucket_size(size_t n) const

3. size_t bucket(const K& key)

功能介绍

1. 返回哈希桶中桶的总个数

2. 返回n号桶中有效元素的总个数

3. 返回元素key所在的桶号

注意:

unordered_set 用法差不多,就不介绍了

3. 哈希概念

通过位置关系找到对应的 key

a. 哈希冲突解决

先说上述问题的解决:

设插入的值为 key,n 为数组的大小

hashi (数组下标) = key % n (若 key 不为整数, 另做处理)

但是在插入的过程中,我们容易遇到以下问题:

  1. hashi 已经有数值存入 (这种问题又叫 哈希冲突

第一种方法:(闭散列

如果是这种情况,直接往后找,直到遇到空位置 (数组里面存入什么值都很难表示这个位置的状态,所以我们自己可以加入)

     2. 所有的位置都有没有位置了

这种问题一定要涉及到空间的开辟 (但是这里我们需要谈论的是什么时候扩空间比较好)

注意:

这种分情况,后面实现再讲

另外一种方法解决(开散列):

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地

址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链

接起来,各链表的头结点存储在哈希表中

4. 闭散列实现

代码

 

enum State
{Empty,Exist,Delete
};
template<class K,class V>
struct HashTable
{pair<K,V> _table;State _state = Empty;
};
template<class K, class V>
class Hsah
{
public:bool insert(const pair<K, V>& kv){if (_t.size() == 0 || n * 10 / _t.size() >= 7){Hsah<K, V> x;size_t size = _t.size() == 0 ? 10 : _t.size() * 2;x._t.resize(size);for (auto i : _t){if (i._state == Exist){x.insert(i._table);}}_t.swap(x._t);}size_t hashi = kv.first % _t.size();int index = hashi;size_t i = 1;while (_t[index]._state != Empty){index = hashi + i;index %= _t.size();i++;}_t[index]._table = kv;_t[index]._state = Exist;n++;return true;}HashTable<K, V>* find(const K& key){if (_t.size() == 0){return nullptr;}size_t hashi = key % _t.size();int index = hashi;size_t i = 1;while (_t[index]._state != Empty){if (_t[index]._table.first == key){if (_t[index]._state == Delete){return nullptr;}return &_t[index];}index = hashi + i;index %= _t.size();i++;if (index == hashi){break;}}return nullptr;}bool Erase(const K& key){HashTable<K, V>* t = find(key);if (t == nullptr || t->_state == Delete){return false;}t->_state = Delete;}
private:size_t n = 0;vector<HashTable<K,V>> _t;
};

5. 开散列实现

代码

template<class K, class V>
struct HashNode
{HashNode(const pair<K, V>& kv):_kv(kv),next(nullptr){}HashNode<K, V>* next;pair<K, V> _kv;
};
template<class K, class V>
class HashBucket
{
public:typedef  HashNode<K, V>  Node;void insert(const pair<K, V>& kv){if (n == _hash.size()){size_t newsize = _hash.size() == 0 ? 10 : _hash.size() * 2;vector<Node*> newnode(newsize, nullptr);for (auto cur : _hash){while (cur){int hashi = cur->_kv.first % newsize;Node* prev = newnode[hashi];newnode[hashi] = cur;cur->next = prev;cur = cur->next;}}_hash.swap(newnode);}int hashi = kv.first % _hash.size();Node* cur = new Node(kv);Node* _next = _hash[hashi];_hash[hashi] = cur;cur->next = _next;n++;}bool find(const K& key){if (_hash.size() == 0){return false;}int hashi = key % _hash.size();Node* cur = _hash[hashi];while (cur){if (cur->_kv.first = key){return true;}cur = cur->next;}return false;}Node* erase(const K& key){int hashi = key % _hash.size();Node* prev = nullptr;Node* cur = _hash[hashi];while (cur){if (cur->_kv.first = key){if (prev == nullptr){_hash[hashi] = cur->next;}else{prev->next = cur->next;}delete cur;break;}prev = cur;cur = cur->next;}return nullptr;}
private:size_t n = 0;vector<Node*> _hash;
};

//这里插入选择了头插

6. unordered_map , unordered_set 底层实现

代码

“test.h” :

#pragma once
template<class K>
struct Compare
{size_t operator()(const K& key){return key;}
};
template<>
struct Compare<string>
{size_t operator()(const string& k){size_t x = 0;for (auto i : k){x += i;x *= 31;}return x;}
};
namespace unordered
{template<class K, class T>struct HashNode{HashNode(const T& data):_data(data), next(nullptr){}HashNode<K, T>* next;T _data;};template<class K, class T, class Key0f, class compare>class HashBucket;template<class K, class T,class Ref,class Ptr, class Key0f, class compare>struct HashIterator{typedef  typename HashIterator<K, T,Ref,Ptr, Key0f, compare> Self;typedef  HashNode<K, T>  Node;HashIterator(Node* hash,const HashBucket<K, T, Key0f, compare>* x):_node(hash),p(x){}Self& operator++(){if (_node->next){_node = _node->next;}else{Key0f kf;int hashi = compare()(kf(_node->_data)) % (p->_hash.size());++hashi;while (hashi < p->_hash.size()){if (p->_hash[hashi]){_node = p->_hash[hashi];break;}else{++hashi;}}if (hashi == p->_hash.size()){_node = nullptr;}}return *this;}Ref operator*(){return _node->_data;}Ptr operator->(){return  &_node->_data;}bool operator!=(const Self& ptr){return _node != ptr._node;}const HashBucket<K, T, Key0f, compare>* p;Node* _node;};template<class K, class T, class Key0f, class compare>class HashBucket{template <class K, class T,class Ref,class Ptr, class Key0f, class compare>friend class HashIterator;public:typedef  HashNode<K, T>  Node;typedef typename HashBucket<K, T, Key0f, compare> Self;typedef typename HashIterator<K, T,T& ,T* ,Key0f, compare> Iterator;typedef typename HashIterator<K,T,const T&, const T*, Key0f, compare> const_Iterator;Iterator begin(){for (int i = 0; i < _hash.size(); i++){if (_hash[i]){return Iterator(_hash[i], this);}}return end();}const_Iterator begin() const{for (int i = 0; i < _hash.size(); i++){if (_hash[i]){return const_Iterator(_hash[i], this);}}return end();}Iterator end(){return Iterator(nullptr, this);}const_Iterator end() const{return const_Iterator(nullptr, this);}pair<Iterator,bool> insert(const T& data){Key0f kf;Iterator it = find(kf(data));if (it != Iterator(nullptr,this)){return make_pair(it, false);}if (n == _hash.size()){size_t newsize = _hash.size() == 0 ? 10 : _hash.size() * 2;vector<Node*> newnode(newsize, nullptr);for (auto cur : _hash){while (cur){int hashi = compare()(kf(cur->_data)) % newsize;Node* prev = newnode[hashi];newnode[hashi] = cur;cur->next = prev;cur = cur->next;}}_hash.swap(newnode);}int hashi = compare()(kf(data)) % _hash.size();Node* cur = new Node(data);Node* _next = _hash[hashi];_hash[hashi] = cur;cur->next = _next;n++;return make_pair(Iterator(cur,this), true);}Iterator find(const K& key){Key0f kf;if (_hash.size() == 0){return Iterator(nullptr,this);}int hashi = compare()(key) % _hash.size();Node* cur = _hash[hashi];while (cur){if (kf(cur->_data) == key){return Iterator(cur, this);}cur = cur->next;}return Iterator(nullptr, this);}T* erase(const K& key){Key0f kf;int hashi = compare()(key) % _hash.size();Node* prev = nullptr;Node* cur = _hash[hashi];while (cur){if (kf(cur->_data) == key){if (prev == nullptr){_hash[hashi] = cur->next;}else{prev->next = cur->next;}delete cur;break;}prev = cur;cur = cur->next;}return nullptr;}private:size_t n = 0;vector<Node*> _hash;};}

 

 "my_map.h" :

#pragma once
#include "test.h"
template<class K,class V,class Hash = Compare<K>>
class map
{struct MapOf{const K& operator()(const pair<K,V>& _key){return _key.first;}};
public:typedef typename unordered::HashBucket<K, pair<const K, V>, MapOf, Hash>::const_Iterator  Iterator;typedef typename unordered::HashBucket<K, pair<const K, V>, MapOf, Hash>::const_Iterator  const_Iterator;pair<const_Iterator, bool> insert(const pair<K, V>& data){return  pair<const_Iterator, bool>(const_Iterator(key.insert(data).first._node,&key), key.insert(data).second);}Iterator find(const K& data){return key.find(data);}pair<K, V>* erase(const K& data){return key.erase(data);}const_Iterator begin() const{return key.begin();}const_Iterator end() const{return key.end();}V& operator[](const K& k){pair<const_Iterator, bool> ret = key.insert(make_pair(k,V()));return ret.first->second;}
private:unordered::HashBucket<K, pair<const K, V>, MapOf,Hash> key;
};

 

 "my_set.h" :

#pragma once
#include "test.h"
template<class K,class Hash = Compare<K>>
class set
{struct SetOf{const K& operator()(const K& _key){return _key;}};
public:typedef typename unordered::HashBucket<K, const K ,SetOf, Hash>::const_Iterator  Iterator;typedef typename unordered::HashBucket<K, const K, SetOf, Hash>::const_Iterator  const_Iterator;pair<const_Iterator,bool> insert(const K& data){return pair<const_Iterator, bool>(const_Iterator(key.insert(data).first._node,&key), key.insert(data).second);}Iterator find(const K& data){return key.find(data);}const K* erase(const K& data){return key.erase(data);}const_Iterator begin() const{return key.begin();}const_Iterator  end() const{return key.end();}
private:unordered::HashBucket<K, const K, SetOf,Hash> key;
};

 

 

仿函数实现分析

”test.h“ 中:

其中:

这个完成了偏特化,将 string 可以变成 整型数,方便计算下标 hashi

细节注意

”my_map.h“ 中 :

”my_set.h“ 中 :

确保 map 和 set 都可以使用,

如果类型是 map ,得到的就是 pair<K,V>里面的 K 类型

如果类型是 set ,得到的就是 K 类型

迭代器问题分析

由于在operator++过程中,需要访问HashBucket的成员变量,所以需要友元

且在构造时需要HashBucket类,则需要前置声明

(前置声明)

迭代器注意:

pair<const_Iterator, bool> insert(const pair<K, V>& data){return  pair<const_Iterator, bool>(const_Iterator(key.insert(data).first._node,&key), key.insert(data).second);}

// return pair<const_Iterator, bool>(key.insert(data)) 是错误的

key.insert(data)是 pair<Iterator, bool> 类型,普通迭代器不能转换成 const迭代器

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

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

相关文章

nested exception is dm.jdbc.driver.DMException: 字符串截断

nested exception is dm.jdbc.driver.DMException: 字符串截断 背景问题分析问题解决 背景 今天在日常工作中遇到了一个问题&#xff0c;正常的 insert into操作报错了 ### Cause: dm.jdbc.driver.DMException: 字符串截断 ; 字符串截断; nested exception is dm.jdbc.driver…

element-ui container 组件源码分享

今日简单分享 container 组件的源码实现&#xff0c;从以下两个方面来讲解&#xff1a; 1、container 组件的页面结构 2、container 组件的属性 一、container 组件的页面结构 二、container 组件的属性 1、container 部分的 direction 属性&#xff0c;子元素的排列方向&am…

Redis(哨兵模式)

什么是哨兵机制 问题: redis 主从复制模式下, 一旦主节点由于故障不能提供服务, 需要人工进行主从切换, 同时大量客户端需要被通知切换到新的主节点上, 对于有一定规模的应用来说, 对于人力的资源消耗会很大.解决: 通过哨兵对主从结构进行监控, 一旦出现主节点挂了的情况, 自动…

利用CSS延迟动画,打造令人惊艳的复杂动画效果!

动画在前端开发中是经常遇到的场景之一&#xff0c;加入动画后页面可以极大的提升用户体验。 绝大多数简单的动画场景可以直接通过CSS实现&#xff0c;对于一些特殊场景的动画可能会使用到JS计算实现&#xff0c;通过本文的学习&#xff0c;可以让你在一些看似需要使用JS实现的…

网络编程(现在不重要)

目录 网络编程三要素与InetAddress类的使用 软件架构 面临的主要问题 网络编程三要素&#xff08;对应三个问题&#xff09; InetAddress的使用 TCP与UDP协议剖析与TCP编程案例&#xff08;了解&#xff09; TCP协议 UDP协议 例子 UDP、URL网络编程 URL&#xff1a;&…

求奖金(if)(C语言)

一、N-S流程图&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int I 0;float bonus 0;//提示用户&#xff1b;printf("请输入利润I&#xff1a;");//获取用户值&#xf…

【优选算法专栏】专题十三:队列+宽搜(一)

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

【Linux系统编程】第四弹---基本指令(二)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、echo指令 2、cat指令 3、more指令 4、less指令 4、head指令 5、tail指令 6、时间相关的指令 7、cal指令 8、find指…

包装类的认识

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x1…

如何鉴别品深茶叶的真伪?

鉴别品深茶叶的真伪可以通过以下三点进行判断&#xff1a;第一是看&#xff0c;观察茶叶的颜色和形状是否自然&#xff1b;第二是闻&#xff0c;感受茶叶的香气是否纯净&#xff1b;第三是泡&#xff0c;品尝茶汤的味道是否醇厚。最好的方式还是通过官方访问进行查询&#xff0…

简单的网站-表白墙(前后端交互)

提交信息后&#xff0c;就得到了下面的一行话 但是存在一些问题 在一个网站中&#xff0c;服务器起到的最主要的效果&#xff0c;就是 “存储数据” 因此服务器这边往往也就需要能够提供两种风格的接口。存数据 、取数据 二、实现前后端交互 1&#xff09;先规定此处请求和响…

2024最新面试跳槽,软件测试面试题的整理与解析

今天接着来说说测试工程师面试比较高频的面试题&#xff0c;大家可以通过面试题内的一些解析再结合自己的真实工作经验来进行答题思路的提取、整理。 硬背答案虽可&#xff0c;但容易翻车哦。能够举一反三才是重点&#xff01; 1&#xff1a;请介绍一下UI自动化测试中三种时间等…

ESP32 S3音频开发

1. 音频硬件框架 Codec&#xff1a;音频编解码芯片&#xff0c;一种低功耗单声道音频编解码器&#xff0c;包含单通道 ADC、单通道 DAC、低噪声前置放大器、耳机驱动器、数字音效、模拟混音和增益功能。它通过 I2S 和 I2C 总线与 ESP32-S3-WROOM-1 模组连接&#xff0c;以提供独…

【Web】DASCTF2022.07赋能赛 题解

目录 Ez to getflag Harddisk 绝对防御 Newser Ez to getflag 进来有两个功能&#xff0c;一个查看&#xff0c;一个上传 图片查看功能可以任意文件读 upload.php <?phperror_reporting(0);session_start();require_once(class.php);$upload new Upload();$upload…

最新版idea 合并分支方法

前言 以下是最新版的idea2024&#xff0c;如果有人找不到按键可能是因为版本不同。 操作步骤 看右小角我的分支是submit&#xff0c;现在我要将test合并到我的submit分支上 找到test分支&#xff0c;选择update&#xff0c;这一步会拉取相应分支内容等同于pull 选择merge 选…

SQL系统函数知识点梳理(Oracle)

这里写目录标题 函数系统函数转换函数to_date()to_char()将数值转换成字符格式 添加货币符号将日期转换成字符 其他不常用的转换函数 字符型函数连接函数大小写转换函数大写转换小写转换首字母大写&#xff0c;其余的小写 替换函数去除空格函数截取函数填充函数获取字符长度函数…

【Sql Server】锁表如何解锁,模拟会话事务方式锁定一个表然后进行解锁

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言创建表模拟…

图灵奖得主AviWigderson:随机性与AI深度融合,引领计算科学新篇章

近日&#xff0c;理论计算机科学领域的杰出代表Avi Wigderson教授荣获了享有“计算机界诺贝尔奖”美誉的图灵奖&#xff0c;以表彰他对计算中随机性和伪随机性研究的杰出贡献。这一荣誉不仅彰显了Wigderson教授在计算理论领域的卓越成就&#xff0c;也为当前热门的AI和深度学习…

Dubbo面试回答简单版

一、dubbo特性 超时重试机制地址缓存多版本负载均衡&#xff1a;随机、权重轮询、最少活跃调用、一致性哈希集群容错&#xff1a;失败重试、快速失败、失败安全、失败自动恢复、并行调用、广播服务降级&#xff1a;异常时返回mock 集群容错 FailOver 失败重试&#xff0c;读…

Linux——守护进程

在这篇文章中我介绍了关于tcp网络套接字&#xff0c;关于网络套接字编程的问题我会再次讲述一点东西&#xff0c;然后介绍关于守护进程的知识。 1. 关于网络套接字编程的一些问题 在进行套接字编程时我们一定是得先有套接字&#xff0c;并且我们在使用socket的一些接口时&…