• 【数据结构】Hash表

    【数据结构】Hash表


    Hash表也叫散列表,是一种线性数据结构。在一般情况下,可以用o(1)的时间复杂度进行数据的增删改查。在Java开发语言中,HashMap的底层就是一个散列表。


    1. 什么是Hash表

    Hash表是一种线性数据结构,这种数据结构的底层一般是通过数组来实现的。在进行数据增删改查的时候,Hash表首先通过Hash函数对某个键值进行Hash操作,这个Hash操作会将这个键映射到数组的某个下标,获得下标以后就可以直接对数组中的数据进行操作了。理论上讲,Hash表数据操作的时间复杂度都是O(1)。

    分享图片

    Hash表的底层是通过数组实现的。数据有个特点就是:必须在初始化的时候指定其长度。所以当Hash表中的数据填满之后想继续向里面放数据的话就必须再创建一个容量更大的数组,然后将之前数组中的数组copy到这个新数组中。这个过程是一个耗费性能的操作,因此我们在使用Hash表之前最好估算下数据的容量,尽量避免扩容操作。

    2. Hash函数

    哈希函数又称为散列函数,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。假设输出值域为S,哈希函数的性质如下:

    • 典型的哈希函数都有无限的输入值域;

    • 当哈希函数输入一致时,输出必相同;

    • 当哈希函数传入不同的输入值时,返回值可能一样,也可能不一样;

    • 对于不同的输入所得的输出值会均匀的分布;

    另外,Hash函数还具有如下两个性质:

    • 免碰撞:即不会出现输入 x≠y ,但是H(x)=H(y) 的情况,其实这个特点在理论上并不成立,比如目前比特币使用的 SHA256 算法,会有2^256种输出,如果我们进行2^256 + 1 次输入,那么必然会产生一次碰撞,事实上,通过 理论证明 ,通过2^130次输入就会有99%的可能性发生一次碰撞,不过即使如此,即便是人类制造的所有计算机自宇宙诞生开始一直运算到今天,发生一次碰撞的几率也是极其微小的。

    • 隐匿性:也就是说,对于一个给定的输出结果 H(x) ,想要逆推出输入 x ,在计算上是不可能的。如果想要得到 H(x) 的可能的原输入,不存在比穷举更好的方法。

    常用的Hash函数有:SHA1、MD5、SHA2等

    3. Hash冲突

    对于不同的输入值,Hash函数可能会给出相同的输出,这种情况就叫做Hash冲突。

    哈希冲突是不可避免的,我们常用解决哈希冲突的方法有开放地址法和** 拉链法**。

    3.1 拉链法

    拉链法的核心思想是:如果Hash表的某个位置上发生了Hash冲突(也就是说在将一个元素放置到数组中某个位置的时候,这个位置上已经有其他元素占据了),那么将这些元素以链表的形式存放。

    分享图片

    链表的查询效率是比较低的,所以如果在Hash表的某个位置上发生冲突的次数太多的话,那么这个位置就是一个很长的链表。查询速度较慢。在Java 8中,HashMap做了一个优化,就是当链表长度达到8时,会自动将链表转换成红黑树,查询效率较高(红黑树是一种自平衡的二叉查找树)。

    3.2 开放地址法

    在开放地址法中,若数据不能直接存放在哈希函数计算出来的数组下标时,就需要寻找其他位置来存放。在开放地址法中有三种方式来寻找其他的位置,分别是线性探测、二次探测、再哈希法

    3.2.1 线性探测法

    线性探测的插入比较简单,做法是:首先将元素进行hash映射,如果映射的位置上没有其他元素,就直接在这个位置上插入数据;如果这个位置上已经有数据了,那么判断下个位置上有无数据,如果没有直接插入如果有数据再进行下一次判断,直到找到空位。

    线性探测的查找:先通过键值定位到数组下标位置,然后将这个位置上数据的值和你要查找数据的值对比,如果相等就直接找到了,如果不相等则继续判断下个元素,所有元素遍历完都没找到的话,则不存在。

    线性探测的删除:首先还是通过键值映射到数组某个下标的位置,然后通过数组中元素的值和你要删除的元素的值进行比较,找出你要删除的那个元素。然后将这个位置上的元素删除并设置一个标志位说明这个位置上曾经有过数据(这步大家自己想想为什么要这么做)

    3.2.2 二次探测法

    在线性探测哈希表中,数据会发生聚集,一旦聚集形成,它就会变的越来越大,那些哈希函数后落在聚集范围内的数据项,都需要一步一步往后移动,并且插入到聚集的后面,因此聚集变的越大,聚集增长的越快。这个就像我们在逛超市一样,当某个地方人很多时,人只会越来越多,大家都只是想知道这里在干什么。

    二次探测是防止聚集产生的一种尝试,思想是探测相隔较远的单元,而不是和原始位置相邻的单元。在线性探测中,如果哈希函数得到的原始下标是x,线性探测就是x+1,x+2,x+3......,以此类推,而在二次探测中,探测过程是x+1,x+4,x+9,x+16,x+25......,以此类推,到原始距离的步数平方。

    3.2.3 双哈希法

    双哈希是为了消除原始聚集和二次聚集问题,不管是线性探测还是二次探测,每次的探测步长都是固定的。双哈希是除了第一个哈希函数外再增加一个哈希函数用来根据关键字生成探测步长,这样即使第一个哈希函数映射到了数组的同一下标,但是探测步长不一样,这样就能够解决聚集的问题。

    第二个哈希函数必须具备如下特点

    • 和第一个哈希函数不一样;
    • 不能输出为0,因为步长为0,每次探测都是指向同一个位置,将进入死循环,经过试验得出 stepSize=constant-(key%constant);形式的哈希函数效果非常好,constant是一个质数并且小于数组容量。

    双hash的核心思想是,第二步生成一个随机的探测步长。

    4. Hash表的相关应用

    电脑只有2G内存,怎么在20亿个数据中找到出现次数最多的整数

    首先我们需要确定value的范围,因为这个20亿个数有可能是同一个数,那么value就为20亿次。因此我们最少需要用一个int型的数据来存这个数(Java中int占4个字节);

    同时我们还要确定下这个20亿整数的取值范围是多少。如果取值范围是1~20亿的话,我们也可以用int来存key,如果是更大的取值范围的话,就需要考虑用long来存了。我们以极端坏的情况来考虑下这个问题:也就是20一个数据全是不同的数据,这些数据的取值范围是超过20亿的,因此我们需要用long类型来存key值,应int类型来存value值,20亿条记录的话大概需要26G左右的内存空间。这样的话显然内存不足,因此一次性统计20亿个数风险很大。

    解决方案:将包含有20亿个数的大文件分成16个小文件,利用哈希函数,这样的话,同一个重复的数肯定不会分到不同的文件中去,并且,如果哈希函数足够好,那么这16个文件中不同的数也不会大于2亿(20 / 16)。然后我们在这16个文件中依次统计就可以了,最后进行汇总得到重复数最多的数。(汇总的时候我只需要取出每个小文件中出现次数最多的数,然后将这16个数进行比较就行了)

    问题:如果这个20亿个数都相同怎么判断呢?

    相关文章
    相关标签/搜索
    福彩网天下彩特彩吧 西乌| 嵩明县| 桃园市| 驻马店市| 霍山县| 永安市| 迭部县| 凉城县| 石阡县| 剑阁县| 甘孜县| 刚察县| 云安县| 荔波县| 旬阳县| 体育| 中方县| 加查县| 临汾市| 龙山县| 循化| 永寿县| 大名县| 铁岭市| 景德镇市| 仲巴县| 驻马店市| 东兰县| 景德镇市| 郧西县| 长白| 威信县| 龙江县| 陆河县| 阿勒泰市| 阳山县| 盐边县| 宜阳县| 扬中市| 同江市| 中宁县| 聂拉木县| 甘肃省| 鄂州市| 彩票| 理塘县| 丰宁| 江津市| 玉龙| 阜阳市| 泉州市| 永城市| 通山县| 亳州市| 阳曲县| 高邑县| 色达县| 湖州市| 山东| 西乡县| 天气| 东宁县| 扎兰屯市| 重庆市| 正定县| 彩票| 林西县| 高邑县| 高雄县| 武山县| 布拖县| 麻江县| 镇江市| 聊城市| 牟定县| 深州市| 罗源县| 永川市| 壤塘县| 维西| 新郑市| 安义县| 英德市| 修文县| 乡城县| 南昌市| 商南县| 新安县| 乌鲁木齐县| 鹤山市| 手游| 城市| 周口市| 邻水| 平乡县| 黔东| 宝清县| 博野县| 鄂尔多斯市| 五常市| 岳普湖县| 德清县| 台北县| 增城市| 西贡区| 中方县| 开远市| 芮城县| 金寨县| 铜山县| 吴江市| 运城市| 寻甸| 鲁山县| 景德镇市| 华亭县| 湘乡市| 芮城县| 仁怀市| 南昌市| 威信县| 苏尼特左旗| 文登市| 达孜县| 新绛县| 新乡县| 越西县| 自贡市| 岗巴县| 万年县| 农安县| 昌都县| 许昌县| 沙湾县| 阜宁县| 大足县| 西丰县| 枣庄市| 建瓯市| 铅山县| 许昌市| 浠水县| 西华县| 青州市| 井陉县| 红原县| 公主岭市| 巴青县| 文水县| 大冶市| 阿图什市| 武强县| 江北区| 齐齐哈尔市| 荆门市| 石棉县| 手机| 贡觉县| 兴安县| 民权县| 建湖县| 石嘴山市| 城口县| 浮山县| 永州市| 台安县| 江安县| 曲沃县| 河津市| 大田县| 巴青县| 治多县| 微博| 长岛县| 淮安市| 道真| 新安县| 东山县| 高淳县| 永宁县| 建阳市| 云南省| 唐山市| 泰州市| 恩平市| 双流县| 聂拉木县| 南涧| 偃师市| 颍上县| 阜宁县| 武强县| 黄平县| 贵阳市| 滕州市| 盈江县| 颍上县| 屯昌县| 高淳县| 信阳市| 湛江市| 佛冈县| 郯城县| 莆田市| 繁昌县| 军事| 淮安市| 武乡县| 佛坪县| 新野县| 青海省| 汨罗市| 吉林省| 博爱县| 陕西省| 伽师县| 南平市| 巫山县| 西昌市| 金沙县| 江津市| 乳山市| 西青区| 中西区| 蓝田县| 霍邱县| 阿拉善左旗| 鄂托克旗| 资源县| 岢岚县| 驻马店市| 蒙自县| 张家口市| 四平市| 石城县| 南投县| 阳谷县| 醴陵市| 普宁市| 龙南县| 临安市| 塔河县| 连山| 凯里市| 澄城县| 子长县| 比如县| 宿迁市| 庄浪县| 惠安县| 北流市| 丹东市| 德令哈市| 昂仁县| 永寿县| 吴堡县| 渭南市| 桂林市| 中方县| 获嘉县| 额济纳旗| 威远县| 沭阳县| 绥江县| 泊头市| 麟游县| 多伦县| 界首市| 景德镇市| 利辛县| 花垣县| 晋宁县| 天全县| 依安县| 勃利县| 屏东市| 永平县| 栖霞市| 稻城县| 黔东| 屯留县| 桃江县| 海原县| 鄂伦春自治旗| 大港区| 贡嘎县| 会泽县| 隆昌县| 海宁市| 怀来县| 巨鹿县| 苗栗县| 衡阳县| 漠河县| 浏阳市| 怀安县| 光山县| 策勒县| 连江县| 丰顺县| 石泉县| 汾阳市| 唐山市| 石楼县| 安国市| 拉萨市| 平邑县| 鹤壁市| 青田县| 岚皋县| 辰溪县| 朝阳区| 阳谷县| 洛扎县| 靖边县| 道真| 噶尔县| 临海市| 阳谷县| 昆山市| 右玉县| 三门县| 方山县| 彭水| 沈阳市| 从江县| 闽清县| 旬阳县| 瑞安市| 嵩明县| 河北区| 新沂市| 安福县| 昌乐县| 大兴区| 托克逊县| 新平| 丹寨县| 巫溪县| 西安市| 唐河县| 铁岭市| 大理市| 当涂县| 金沙县| 全椒县| 齐河县| 河池市| 阿坝| 资讯| 和顺县| 奉化市| 静安区| 平阳县| 太谷县| 清徐县| 峨眉山市| 金川县| 云浮市| 石城县| 深州市| 富源县| 东阳市| 高清| 区。| 长顺县| 库伦旗| 外汇| 元江| 湄潭县| 兴宁市| 随州市| 枣阳市| 景德镇市| 黔西| 神池县| 岳阳县| 平泉县| 吉安县| 贵港市| 错那县| 喀喇沁旗| 奉贤区| 依安县| 绍兴市| 安图县| 左权县| 南木林县| 马关县| 新丰县| 江山市| 敖汉旗| 旬邑县| 佛冈县| 泽库县| 临朐县| 温宿县| 射阳县| 博白县| 子洲县| 车险| 当雄县| 建水县| 南溪县| 寻乌县| 龙游县| 高台县| 邵东县| 定州市| 社旗县| 金塔县| 吴江市| 巫溪县| 青冈县| 泰宁县| 台山市| 定边县| 九龙城区| 深水埗区| 彭水| 叶城县| 巴青县| 清丰县| 朔州市| 文水县| 永新县| 犍为县| 长沙县| 贺兰县| 庆元县| 华坪县| 霍林郭勒市| 伽师县| 吴桥县| 克什克腾旗| 蓬莱市| 绥中县| 双城市| 密山市| 娄烦县| 宁城县| 任丘市| 灌阳县| 新化县| 富锦市| 三江| 定州市| 新乡县| 嘉黎县| 海城市| 黄冈市| 贵州省| 平昌县| 晋江市| 颍上县| 漳平市| 海林市| 建阳市| 青河县| 临颍县| 红桥区| 津市市| 枣庄市| 宁阳县| 高碑店市| 兴城市| 宁阳县| 织金县| 石屏县| 西林县| 濮阳市| 南乐县| 宿松县| 秦皇岛市| 通州区| 富蕴县| 通山县| 麟游县| 陆河县| 清涧县| 卢湾区| 丹巴县| 舟曲县| 柞水县| 乃东县| 措勤县| 同心县| 谢通门县| 金昌市| 玉林市| 五莲县| 兰溪市| 司法| 广州市| 鄂托克旗| 剑河县| 茌平县| 同仁县| 阿巴嘎旗| 涡阳县| 淮滨县| 万山特区| 石柱| 金沙县| 酒泉市| 日喀则市| 隆昌县| 宜昌市| 固镇县| 建昌县| 芷江| 色达县| 定南县| 馆陶县| 延长县| 六安市| 蓝田县| 呼伦贝尔市| 平凉市| 秦皇岛市| 望城县| 高碑店市| 威海市| 米林县| 德格县| 锡林浩特市| 赫章县| 柳江县| 邵阳县| 临高县| 铜鼓县| 营口市| 博乐市| 黄陵县| 城口县| 兴城市| 延安市| 容城县| 泉州市| 上思县| 高要市| 东兴市| 和硕县| 山丹县| 钟山县| 绥芬河市| 城固县| 白山市| 万源市| 长子县| 万安县| 永顺县| 彭山县| 兴义市| 新晃| 兴海县| 尼玛县| 聊城市| 莱芜市| 射阳县| 应用必备| 潮州市| 侯马市| 南江县| 梅河口市| 达尔| 广宁县| 平陆县| 四平市| 株洲县| 山丹县| 疏勒县| 太谷县| 安徽省| 宿松县| 安图县| 萍乡市| 宿迁市| 隆德县| 惠水县| 云霄县| 巧家县| 宿松县| 广汉市| 于田县| 颍上县| 仁寿县| 太保市| 抚顺市| 龙岩市| 贡觉县| 大同市| 关岭| 峨边| 宜兰市| 静宁县| 陆河县| 崇明县| 台东市| 赤水市| 武邑县| 永寿县| 安义县| 尖扎县| 阜宁县| 广东省| 保山市| 嵩明县| 观塘区| 台湾省| 独山县| 马山县| 信丰县| 芜湖县| 博湖县| http://wap.fqxrux.fit http://m.hzpgnf.fit http://wap.jmijkq.fit http://napphw.fit http://www.sqffuc.fit http://vpmpxr.fit http://wap.makpvk.fit http://bm1961xailz.fit http://m.xsspwq.fit http://www.dkbnwe.fit http://m.dfdpqy.fit http://www.nvebuf.fit http://www.pcjqlo.fit http://www.ewrokd.fit http://m.bm1961xountz.fit http://m.ojscti.fit http://wap.jwnmti.fit http://m.ddvlpt.fit