散列表数据结构

May 11, 2019

在实际应用中我们需要用到一种动态集合结构。散列表是普通数组的推广,可以对数组直接寻址,故可以在O(1)时间内访问数组的任意元素。散列表中的思想是把关键字映射到数组下标,是通过计算出来的,散列计算过程中容器出现碰撞,解决碰撞也是散列的一个难点,根本问题是哈希计算的问题。

直接访问表

当需要存储一个(1.....m-1)的元素时,并且m比较小,此时就可以用一个数组T来存储所有从1-m之间的数组。 比如需要存储:{4,6,8,3}这几个元素,这里面最大的元素为8,所以创建一个数组有8个元素,把对应的元素放到数组槽里面即可。

直接寻址有一个问题就是当m很大是,需要的数组T就会很大,即使需要存储的元素比较少,比如:{4,1,6,2^64},这里只有四个元素,最大值为2^64,如果创建一个T的数组则需要很大的存储空间。

散列表原理和实现

全域散列

开放寻址法

go map实现

golang中map是一个kv对集合。底层使用hash table,用链表来解决冲突,通过编译器配合runtime,所有的map对象都是共用一份代码。

hash 函数

hash函数通过cpu的架构来选择不同的计算方式,有aesmemhash两种实现方式,memhash是借鉴xxhashcityhash原理进行开发,其速度超级快,且采用的是随机哈希的方式,在每一运行过程中都有一个种子元素,同一个key哈希出来的值都是不一样的,这样就避免了哈希碰撞攻击, 源码在src/runtime/hase64.go文件中,

解决碰撞

map中主要通过bmap这个结构来存储k/v的,bmaphmap中是一个动态数组,每个bmap对象由8个k/v组成,如果一个bmap填充满了,bmap中有一个指向下一个bmap的指针,这样就通过链表解决了冲突的情况。

参考文章


LRF 记录学习、生活的点滴