永州做网站费用,网站建设学费,南京地区网站开发,安康教育平台哈希表
来吧#xff01;一文彻底搞定哈希表#xff01; - 知乎 (zhihu.com)
百科解释#xff1a; “散列表#xff08;Hash table#xff0c;也叫哈希表#xff09;#xff0c;是根据键#xff08;Key#xff09;而直接访问在内存存储位置的数据结构。也就是说…哈希表
来吧一文彻底搞定哈希表 - 知乎 (zhihu.com)
百科解释 “散列表Hash table也叫哈希表是根据键Key而直接访问在内存存储位置的数据结构。也就是说它通过计算一个关于键值的函数将所需查询的数据映射到表中一个位置来访问记录这加快了查找速度O(1)。这个映射函数称做散列函数存放记录的数组哈希表本质上是数组称做散列表。 实现方式
数组 链表数组 二叉树
散列函数
由关键值key加工获得数据存放位置 常见的散列函数
一文看懂哈希表并学会使用C STL 中的哈希表_哈希表end函数-CSDN博客
1线性定址法直接取关键字的某个线性函数作为存储地址散列函数为 Hash(key)a×keyb 优点简单、均匀 缺点需要事先知道关键字的分布情况如果关键字的分布比较分散会很浪费空间 使用场景适合查找比较小且连续的情况 2除留余数法将关键字对某一小于散列表长度的数p取余的结果作为存储地址散列函数为 Hash(key)keymodp 缺陷 适用于整数的存储字符串、浮点数不能直接存储因为不能直接取模后面会讲如何解决余数相同时会出现哈希冲突 3平方取中法对关键字取平方然后将得到结果的中间几位作为存储地址 4折叠法将关键字分割为几部分然后将这几部分的叠加和作为存储地址。
处理哈希冲突
闭散列 -- 开放寻址法
①线性探测法找该位置的后一个位置直到找到空位置为止当查看到存储空间的末尾时还是找不到空位置就返回从头开始查看 缺点 如果某个位置冲突的多会导致一片冲突很多数据堆积在一起。 插入和查找的效率都会降低很多插入元素时从冲突位置开始不断往后找到下一个空位置查找元素时从冲突位置开始不断往后找需要比较许多次导致搜索效率降低。最坏情况下要直到找到空位置时才能说明没有该元素。 ②平方探测法不同于前面线性探测法依次顺序查看下一个位置是否能存储元素平方探测的规则是以1^2,-1^2,2^2,-2^2,...,探测新的存储位置能否存储元素 如果一个位置有很多数据冲突那么二次探测会让这些数据存储位置会比较分散不会集中在一起导致一片一片的冲突。 ③再散列法利用两个散列函数当通过第一个散列函数得到关键字的存储地址发生冲突时再利用第二个散列函数计算出地址增量地址计算方式如下 Hi(Hash1(key)i∗Hash2(key))%p ④伪随机数法 当发生地址冲突时加入一个随机数作为地址增量寻找新的存储地址地址计算方式如下 Hi(Hash(key)di)%p,其中di为随机数 拉链法
链表长度大于等于8的话链表就会转换成二叉树小于等于6就会还原链表空一个7是为了避免频繁的转换链表和树消耗性能
哈希表的扩容
为了避免过于频繁的哈希冲突当占的位置达到负载因子0.7时就会扩容为原来的二倍还需要把原来数组的所有键值对重新 Hash 一遍放到新的数组
如何使用STL库中的哈希表
1导入头文件 #includeunordered_map 2哈希表的声明和初始化 1声明
unordered_mapelemType_1, elemType_2 var_name; //声明一个没有任何元素的哈希表
//其中elemType_1和elemType_2是模板允许定义的类型如要定义一个键值对都为Int的哈希表
unordered_mapint, int map;2初始化 以上在声明哈希表的时候并没有给unordered_map传递任何参数因此调用的是unordered_map的默认构造函数生成一个空容器。初始化主要有一下几种方式 a在定义哈希表的时候通过初始化列表中的元素初始化:
unordered_mapint, int hmap{ {1,10},{2,12},{3,13} };
//如果知道要创建的哈希表的元素个数时也可以在初始化列表中指定元素个数
unordered_mapint, int hmap{ {{1,10},{2,12},{3,13}},3 };b通过下标运算来添加元素:
//当我们想向哈希表中添加元素时也可以直接通过下标运算符添加元素格式为: mapName[key]value;
//如hmap[4] 14;
//但是这样的添加元素的方式会产生覆盖的问题也就是当hmap中key为4的存储位置有值时
//再用hmap[4]value添加元素会将原哈希表中key为4存储的元素覆盖
hmap[4] 14;
hmap[4] 15;
cout hmap[4]; //结果为15c通过insert()函数来添加元素:
//通过insert()函数来添加元素的结果和通过下标来添加元素的结果一样不同的是insert()可以避免覆盖问题
//insert()函数在同一个key中插入两次第二次插入会失败
hmap.insert({ 5,15 });
hmap.insert({ 5,16 });
cout hmap[5]; //结果为15d复制构造通过其他已初始化的哈希表来初始新的表:
unordered_mapint, int hmap{ {1,10},{2,12},{3,13} };
unordered_mapint, int hmap1(hmap);STL中哈希表的常用函数
(1) begin( )函数该函数返回一个指向哈希表开始位置的迭代器
unordered_mapint, int::iterator iter hmap.begin(); //申请迭代器并初始化为哈希表的起始位置
cout iter-first : iter-second;(2) end( )函数作用于begin函数相同返回一个指向哈希表结尾位置的下一个元素的迭代器
unordered_mapint, int::iterator iter hmap.end();(3) cbegin() 和 cend()这两个函数的功能和begin()与end()的功能相同唯一的区别是cbegin()和cend()是面向不可变的哈希表
const unordered_mapint, int hmap{ {1,10},{2,12},{3,13} };
unordered_mapint, int::const_iterator iter_b hmap.cbegin(); //注意这里的迭代器也要是不可变的const_iterator迭代器
unordered_mapint, int::const_iterator iter_e hmap.cend();(6) erase()函数 删除某个位置的元素或者删除某个位置开始到某个位置结束这一范围内的元素 或者传入key值删除键值对
unordered_mapint, int hmap{ {1,10},{2,12},{3,13} };
unordered_mapint, int::iterator iter_begin hmap.begin();
unordered_mapint, int::iterator iter_end hmap.end();
hmap.erase(iter_begin); //删除开始位置的元素
hmap.erase(iter_begin, iter_end); //删除开始位置和结束位置之间的元素
hmap.erase(3); //删除key3的键值对(7) at()函数根据key查找哈希表中的元素
unordered_mapint, int hmap{ {1,10},{2,12},{3,13} };
int elem hmap.at(3);(8) clear()函数清空哈希表中的元素
hmap.clear()(9) find()函数以key作为参数寻找哈希表中的元素如果哈希表中存在该key值则返回该位置上的迭代器否则返回哈希表最后一个元素下一位置上的迭代器
unordered_mapint, int hmap{ {1,10},{2,12},{3,13} };
unordered_mapint, int::iterator iter;
iter hmap.find(2); //返回key2的迭代器可以通过iter-second访问该key对应的元素
if(iter ! hmap.end()) cout iter-second;(10) bucket()函数以key寻找哈希表中该元素的储存的bucket编号unordered_map的源码是基于拉链式的哈希表所以是通过一个个bucket存储元素
int pos hmap.bucket(key);(11) bucket_count()函数该函数返回哈希表中存在的存储桶总数一个存储桶可以用来存放多个元素也可以不存放元素并且bucket的个数大于等于元素个数
int count hmap.bucket_count();(12) count()函数 统计某个key值对应的元素个数 因为unordered_map不允许重复元素所以返回值为0或1
int count hmap.count(key);【C STL】哈希 Hash闭散列、开散列介绍及其实现_c stl hash-CSDN博客
顺序结构以及二叉平衡树中元素关键码与其存储位置之间没有对应的关系因此在查找一个元素时必须要经过关键码的多次比较。顺序结构的查找时间复杂度为O(N)二叉平衡树中查找时间复杂度为树的高度O(log2N)搜索的效率取决于搜索过程中元素的比较次数。 红黑树