散列表——Hash Table

前文对数组和链表进行了分析,介绍了各自在插入、遍历、删除方面的优势和劣势,时间复杂度如下表示。

 数组链表
读取O(1)O(n)
插入O(n)O(1)
删除O(n)O(1)

既然数组和链表都存在一定的不足,那么有没有什么方法能在快速读取的同时,可以快速的将数据插入呢?答案就是散列表!


场景

假设你开了一家超市,但还没有采购收银机器,于是在顾客结账的时候只能到价格列表上一个个查找。如果这个列表有序且商品数量不多,你只需要进行O(nlogn)次查找即可。可一旦顾客买了十几甚至几十种商品时,这就比较费劲了。一旦慢下来,后面那么长的队伍还在等着你呢,那么有没有什么办法能更快的做查询呢?一个好办法就是找一个熟记价格的营业员,对n件商品只需O(n)时间即可。那么问题又来了,怎么找到这么优秀的雇员呢?!接下来就要看散列表出场了!


定义&原理

定义:对于给定输入,能稳定的给出相应的输出(不能每次都不一样)。

那散列表又是如何存储的呢?为了达到理想的O(1)查找时间,能满足这个要求的非数组莫属了。但是数组是按照index进行索引,那又如何将输入的数字或字符串(如“book”、“mouse”等)转换为索引呢,即f(input)=index,且一个input对应一个唯一的index。这就是散列函数所要干的事情,有关的见SHA算法。

冲突

但是对于再优秀的散列函数,也可能存在有缺陷的地方,即存在多个给定输入,产生同一个输出的情况。之前MD5、SHA算法都已先后被攻破(新闻)。

缺陷归缺陷,当出现冲突的时候,是不是又可以用上其他方法来解决呢?常见的冲突解决方法有开放定址法,链地址法,建立公共溢出区等。实际的哈希表实现中,使用最多的是链地址法,如下。

在这种情况下的时间复杂度如下(差了可不是一点半点):

 平均情况最坏情况
读取O(1)O(n)
插入O(1)O(n)
删除O(1)O(n)

 

装填因子

因哈希表的存储介质是数组,而一个固定了大小的数组不可能存储无限多的数据,因此需要考虑什么时候对数组进行扩容。装填因子干的就是这个事情,告诉散列表什么时候预先进行扩容。


实际应用

DNS解析:域名->IP转换(上亿个网页的域名解析,不用散列表,有其他更高效的方法吗?)

数据缓存——对于要频繁访问的数据,从磁盘载入到内存的成本高昂,何尝不直接就放在内存里呢?


总结

散列表和数组、链表一样,都有其优劣之处。各取其长,来解决更大的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值