1.哈希

概念

可以不经过任何比较,直接从表中得到要搜索的元素。 关键在于通过某种散列函数,使元素的存储位置与它的关键码之间能够建立 一一映射的关系。这样就可以通过o(1)的时间复杂度来寻找到元素。

例如数据集合{1,7,4,5,9,6},哈希函数hash(key)=key&capacity

 冲突

hash(7)=7 hash(17)=7,两个不同的数通过哈希函数映射到了一个位置,产生了冲突。哈希函数设计的越精妙,产生冲突的可能性就越低,但无法避免

解决方法:

  • 闭散列(开放定址法)
  • 开散列(拉链法)

2.散列

2.1 散列查找的基本思想

在记录的存储位置和它的关键码之间建立一个确定的对应关系H,使得每个关键码key和唯一的存储位置H(key)相对应。

采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表(hash table),将关键码映射为散列表中适当存储位置的函数称为散列函数(hash function),所得到的存储位置成为散列地址(hash address)。

2.2 常见的散列函数

直接定址法

集合为{10,30,50,70,80,90},选取H(key) = key/10

直接定址法的特点是不会产生冲突,但实际应用中能使用这种散列函数的情况很少。

它适用于事先知道关键码的分布,关键码集合不是很大且连续性较好的情况。


除留余数法

选择某个适当的正整数p,以关键码除以p的余数作为散列地址,H(key) = key mod p


平方取中法 

平方取中法是对关键码平方后,按散列表大小,取中间若干位作为散列地址(简称平方后截取),其原理是一个数平方后,中间的几位分布较均匀,从而冲突发生的概率较小。

对于关键码1234,假设散列地址是2位,由于1234×1234=1522756,选取中间的两位作为散列地址,可以选22也可以选27。

通常用在事先不知道关键码的分布且关键码的位数不是很大的情况

2.3 处理冲突的办法

开放定址法(线性探测/闭散列) 本篇博文后续实现

链地址法(哈希桶/开散列) 见如下博文C++--哈希表--开散列(哈希桶的模拟实现)--1110_Gosolo!的博客-CSDN博客

多重散列法

3. 闭散列

闭散列,(开放定址法)发生冲突时,如果哈希表没有被填满,则表内一定还有其他空闲位置,可以把冲突值放到下一个没有被占用的空余位置上。

如何找到下一个没有被占用的空位?答:采用线性探测方法。从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

3.1 线性探测

 线性探测的插入

如:在上述的哈希表中插入元素44,由于下标为4的位置放入了元素4,于是从该位置往后++,找到第一个不为空的位置,将44放入。

 线性探测的删除

在寻找要删除的元素时,依然会根据存放在哈希表的下标开始寻找,比如在上述哈希表中寻找4,在4下标位置直接就可以找到该元素。但如果直接将其删除,那后续寻找元素44时,就会因为4下标没有元素,而认为元素44不存在于这张哈希表。所以我们需要设置一个状态来表示删除。

3.2 哈希表闭散列的模拟实现

我们写在一个自定义类域 Closehash 里面

3.2.1 准备工作

哈希表中元素状态

namespace Closehash
{//哈希表中元素的状态enum State{EMPTY,EXIT,DELETE};
}

存储类型用pair即可,但是数据中要包含状态,我们进行一次封装

//由于数据需要一个状态,所以需要将pair<K,V>封装一层template<class K,class V>struct HashDate{pair<K, V>_kv;State _state;};

开始(画饼)构建哈希表的内容

template<class K,class V>class HashTable{public:bool Insert(const pair<K,V>& kv);HashDate<K, V>* find(const K& key);bool Erase(const K& key);private:vector<HashDate<K,V>> _tables;size_t _size = 0;};

3.2.2 闭散列的插入

     bool Insert(const pair<K, V>& kv){//if (Find(kv.first)) return false; //Find实现了再去掉注释if (_tables.size() == 0 || 10 * _size / _tables.size() >= 7)//相当于存了70%{//开始扩容size_t newsize = _tables.size()== 0 ? 10 : _tables.size() * 2;HashTable<K, V> newHash;newHash._tables.resize(newsize);for (auto e: _tables)//注意_tables是HashDate类型 里面有_kv 和_state{if (e._state == EXIST){newHash.Insert(e._kv);}}//资本家拷贝方法_tables.swap(newHash._tables);}//走到这里扩容完成 或者空间足够大size_t hashi = kv.first % _tables.size();//寻找在表中对应的下标是什么while (_tables[hashi]._state==EXIST){hashi++;//走到头了得回来hashi%=_tables.size();}_tables[hashi]._kv = kv;_tables[hashi]._state = EXIST;_size++;return true;}

测试用例

void TestHT1(){int a[] = { 1, 11, 4, 15, 26, 7, 44 };HashTable<int, int> ht;for (auto e : a){ht.Insert(make_pair(e, e));}ht.Print();}

添加个99以验证扩容功能

3.2.3 闭散列的查找

HashDate<K, V>* Find(const K& key){if (_tables.size() == 0) return nullptr;size_t hashi = key % _tables.size();while (_tables[hashi]._state != EMPTY){if (_tables[hashi]._state != DELETE && _tables[hashi]._kv.first == key){return &_tables[hashi];}hashi++;hashi% _tables.size();}return nullptr;}

 测试用例

 cout << ht.Find(4)->_kv.first << endl; 

3.2.4 闭散列的删除

bool Erase(const K& key){HashDate<K,V>* ret = Find(key);if (ret){ret->_state = DELETE;--_size;return true;}else{return false;}}

3.3 模拟实现的闭散列中的问题与改进

上述测试用例中使用的是pair<int,int>那我要是用pair<string,int>呢?我的key还可以直接对数组长度取模吗?或者key是一个更复杂的结构体类型呢?

文档中对这一问题,给我们预留一个空间,模板中有一个参数可以用来传入仿函数。

template < class Key,   class T,  class Hash = hash<Key> >

我们这里也按照该方法模拟一个。

 template<class K>struct HashFunc{size_t operator()(const K& key){return (size_t)key;}};// 特化  template<>struct HashFunc<string>{// BKDRsize_t operator()(const string& key){size_t val = 0;for (auto ch : key){val *= 131;val += ch;}return val;}};template<class K,class V,class Hash=HashFunc<K>>class HashTable{public:bool Insert(const pair<K,V>& kv);HashDate<K, V>* find(const K& key);bool Erase(const K& key);private:vector<HashDate<K,V>> _tables;size_t _size = 0;};

在每次求 在哈希表中位置的前面添加

Hash hash;

size_t hashi = hash(kv.first) % _tables.size()

测试用例

void TestHT2(){string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };//HashTable<string, int, HashFuncString> countHT;HashTable<string, int> countHT;for (auto& str : arr){auto ptr = countHT.Find(str);if (ptr){ptr->_kv.second++;}else{countHT.Insert(make_pair(str, 1));}}}

测试用例没加打印...让我来回看了好几遍代码...蠢到无语

4. 问答

1. 哈希就是一种用于高效查找的数据结构。

2. 哈希之所以高效就是采用了哈希函数(散列函数),将元素和其存储位置建立了一一映射的关系,所以哈希必须使用哈希函数。

3. 哈希冲突是不可能避免的

4. 已知某个哈希表的n个关键字具有相同的哈希值,如果使用二次探测再散列法将这n个关键字存入哈希表,至少要进行()次探测。

元素1:探测1次

元素2:探测2次

元素3:探测3次

。。。

元素n:探测n次

故要将n个元素存入哈希表中,总共需要探测:1+2+3+...+n = n*(n+1)/2

5. 采用开放定址法处理散列表的冲突时,其平均查找长度高于链接法处理冲突。

开放定址法一旦产生冲突,冲突容易连在一起,引起一连篇的冲突,链地址法一般不会

6. 线性探测采用未删除法,当从哈希表中删除某个元素时,并没有将该元素真正的删除掉,而是采用标记的方式处理,但是不能直接将该位置标记为空,否则会影响从该位置产生冲突的元素的查找。

C++--哈希表--散列--冲突--哈希闭散列模拟实现--问答--1107相关推荐

  1. 哈希表and处理冲突的方法

    哈希法又称散列法.杂凑法以及关键字地址计算法等,相应的表称为哈希表.这种方法的基本思想是:首先在元素的关键字k和元素的存储位置p之间建立一个对应关系f,使得p=f(k),f称为哈希函数.创建哈希表时, ...

  2. 查找算法【哈希表】 - 处理冲突的方法:开放地址法-线性探测法

    查找算法[哈希表] - 处理冲突的方法 无论如何设计散列函数,都无法避免发生冲突. 如果发生冲突,就需要处理冲突. 处理冲突的方法分为3种: 开放地址法 链地址法 建立公共溢出区. [开放地址法] 开 ...

  3. 什么是哈希表?什么又是哈希冲突?哈希冲突的解决方法?

    首先,什么是哈希表?什么又是哈希冲突? ①哈希表是基于数组的一种存储方式.它主要由哈希函数和数组构成.当要存储一个数据的时候,首先用一个函数计算数据的地址,然后再将数据存进指定地址位置的数组里面.这个 ...

  4. 哈希表题目:设计哈希映射

    文章目录 题目 标题和出处 难度 题目描述 要求 示例 数据范围 前言 解法一 思路和算法 代码 复杂度分析 解法二 思路和算法 代码 复杂度分析 题目 标题和出处 标题:设计哈希映射 出处:706. ...

  5. 【哈希表】(一) 设计哈希表

    目录 一.设计哈希表 二.设计哈希表的关键 三.设计哈希集合 3.1 题目要求 3.2 解决过程 四.设计哈希映射 4.1 题目要求 4.2 解决过程 五.设计哈希表 - 解决方案 六.复杂度分析 - ...

  6. 在java中 哈希表会经常出现哈希碰撞吗

    在Java中,哈希表可能会经常出现哈希碰撞.哈希表是一种根据键(Key)来访问值(Value)的数据结构,通过哈希函数将键映射到哈希表的索引位置上.由于哈希函数的映射结果可能不唯一,不同的键可能会被映 ...

  7. 哈希表建立及冲突处理

    1.哈希表  数据储存在一片连续区域:单个节点保存键名(用于校验)和键值(目标数据). 2.键名通过哈希函数变成索引,索引指定该数据储存位置. 3.常见的哈希函数方法有: 对于数值:线性变换,选取若干 ...

  8. 哈希表数据结构_Java数据结构哈希表如何避免冲突

    前言 一.哈希表是what? 这是百度上给出的回答: 简而言之,为什么要有这种数据结构呢? 因为我们想不经过任何比较,一次从表中得到想要搜索的元素.所以就构造出来了哈希表,通过某种函数(哈希函数)使元 ...

  9. Python数据结构实战——哈希表中的冲突处理(Collision Handling In Hash Table)

    文章目录 1.定义哈希类(能处理冲突) 2.测试 2.1.增加键值 2.2.查找对应键值 2.3.查看哈希表 2.4.更新值 2.5.删除键值对 1.定义哈希类(能处理冲突) class HashTa ...

最新文章

  1. python del函数_python del函数是什么以及如何使用?
  2. 索引节点(inode)爆满问题处理
  3. vue e-charts按需引入
  4. exchange 20132016配置使用IMAPPOP
  5. [LAMP]——mod_security和mod_evasive模块的安装
  6. 关于协方差矩阵的理解
  7. php限制密码输入错误次数,js密码输入错误指定次数禁止输入
  8. MATLAB中的imagesc
  9. 大数据时代,CRM帮助企业进行升级转型
  10. 最著名的数学家一般也是最著名的力学家
  11. 使用运放构建电压跟随器
  12. 前端周刊第56期:应接不暇的技术大会
  13. Matrix TraceCanary
  14. Vue 项目中各种痛点问题及方案(建议收藏)
  15. -1073740791 (0xC0000409)错误,附加内容:qt布局、页面跳转
  16. macbook系统占用硬盘大_苹果电脑系统占用硬盘过大,怎么解决
  17. 解决“File has been changed outside the editor, reload?”提示
  18. windows硬盘数据安全处理工具
  19. 九宫格构图学习[1]
  20. 什么认证在云计算行业内的含金量最大?考试费用贵不贵?

热门文章

  1. iOS安全之在模拟器中安装APP
  2. vscode“检测到#include 错误请更新includepath”的解决方案
  3. IntelliJ IDEA常用快捷键大全
  4. java sublist_java 中sublist的使用
  5. AndroidTV 模拟器的搭建
  6. c语言double类型printf问题
  7. 批量将Txt文件内容拆分成Excel文件
  8. 计算机考研分数403,初试403分,我的考研经验全在这
  9. 5G上行,真是让人操碎了心!
  10. 科技云报道:巨头下场,生态聚合,低代码不再是个伪命题