在百度上搜索哈希(hash),你将会知道:将任意长度的输入(关键字),通过Hash算法变成固定长度的输出。这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串就是哈希值,通常哈希值代表了关键字的存储位置。
但是为什么要这样做呢?或者说,哈希是怎样来的呢?
个人觉得哈希的出现解决了两个问题:存储和搜索。
1.存储(数据结构):如果在容器中保存对象及其关联的键,并且不用键来决定 (键/对象) 对的顺序,那就必须对键值釆用其他方式来确定元素在内存中的位置。
如果使用像 string 这样的对象作为键,就会遇到一些问题,可能的变量的数目是巨大的。具有 10 个字符的字母字符串可能的个数是 26^10。这个索引范围没有多大用处。我们需要一种机制来将它变为可接受的范围;而且理想情况下,这个机制可以为每个键生成唯一的值,根据这个值所在的位置来找到对应的字符串。这正是哈希需要做的事情之一。
2.搜索(算法/思想):当我们处理数据的时候,不外乎使用顺序结构、链表和树结构。但是使用这些结构时,元素的关键字与其存储位置之间通常没有对应的逻辑关系,这时候想要查找一个元素,我们根本不知道它在结构中的存储位置,甚至不知道它在不在结构中,这要求我们必须要不断地比较关键字进行查找,如顺序结构查找的时间是O(N),树结构是O(logN),通常这带来的结果是不断for循环遍历,极大地增加了代码时间复杂度。
那么可不可以有更好的搜索方法呢?哈希函数应运而生:由上面的定义可知,hash完美解决了这一问题,可以不经过任何比较,直接从哈希表/集中得到要搜索的元素或判断是否存在。
哈希有两种缺点:实际缺点和根本缺点。
1.根本缺点:
哈希表的缺点它是基于数组的,数组创建后难于扩展某些哈希表被基本填满时,性能下降得非常严重,所以程序虽必须要清楚表中将要存储多少数据(或者准备好定期地把数据转移到更大的哈希表中,这是个费时的过程)。
而且,也没有一种简便的方法可以以任何一种顺序〔例如从小到大〕遍历表中数据项。如果需要这种能力,就只能选择其他数据结构。
然而如果不需要有序遍历数据,井且可以提前预测数据量的大小。那么哈希表在速度和易用性方面是无与伦比的。
2.实际缺点:
哈希冲突:由于哈希算法被计算的数据是无限的,而计算后的结果范围有限,因此总会存在不同的数据经过计算后得到的值相同,这就是哈希冲突,即不同的关键字得到的值相同。
这样的话我们前面所有的理想情况就成了笑谈,所以处理问题时要解决哈希冲突。
基本原则:便于计算(函数计算时间<=查找时间);(散列地址)分布均匀
直接定值法是取关键字的某个线性函数值为散列地址,即 f(key) = a*key + b。其优点是简单、均匀,不会产生冲突;但缺点是需要知道关键字的分布情况,希望数值是连续的。
数字分析法通常适合处理关键字位数比较大的情况,例如我们现在要存储外卖信息登记表,如果用手机号作为关键字,那么抽取后面的四位数字作为散列地址是不错的选择。
平方取中法是将关键字平方之后取中间若干位数字作为散列地址。这种方法适用于不知道关键字的分布,且数值的位数又不是很大的情况。
折叠法是将关键字从左到右分割成位数相等的几部分,然后将这几部分叠加求和,并按散列表表长取后几位作为散列地址。
此方法最常用:
f(key) = key mod p(p <= n) n:散列表的长度。
p的选择非常非常非常重要。
f(key) = random(key)
当关键字的长度不等时,采用这个方法构造散列函数是比较合适的。
当发生哈希冲突时,寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,处理冲突的过程结束,记录填入的位置:
fi(key) = (f(key)+di) MOD n
di为增量序列,可以有下列三种取法:
1.di = 1,2,...,n-1:线性探测再散列;
2.di = 1^2,-1^2,...,k^2,-k^2(k<=n/2) 二次探测再散列
3.di = 伪随机数序列:伪随机探测再散列
没有冲突的元素放在基本表,有冲突的元素,将多余的元素放在冲突表。
深度剖析C++无序容器的底层实现机制 (biancheng.net)
1.以键值对(pair类型)的形式存储数据
2.存储的各个键值对的键互不相同且不允许被修改
3.不会自行对存储的键值对进行排序
#include
#include //哈希表头文件
using namespace std;
int main(){
//建立哈希表
unordered_map hashsmap
//第一个Type是键的变量类型,第二个是值得变量类型,hashmap是该哈希表的名称
//插入键值对的两种方法
hashmap.insert(make_pair(key,value));
hashmap[key] = value;
//删除键值对
hashset.erase(key)
//查询键值
cout<0) cout<<"exist"<first << "," << i->second << ") "<
无序哈希集是一种功能简单的哈希结构:
1.不再以键值对的形式存储数据,而是直接存储数据的值(存储的键值对 键和值相同 所以只存value即可);
2.容器内部存储的各个元素的值都互不相等,且不能被修改;
3.不会自行对内部存储的数据进行排序。
#include
#include //哈希集头文件
using namespace std;
int main(){
//建立哈希集
unordered_set hashset
//Type是哈希集中键的变量类型,hashset是该哈希集的名称
//插入键
hashset.insert(key)
//删除键
hashset.erase(key)
//搜索键
if(hashset.count(key) > 0) cout<<"exist"<