哈希表
哈希表也叫散列表,是根据关键码值(Key Value)而直接进行访问的数据结构,它通过把关键码值映射到哈希表中的一个位置来访问记录,以加快查找的速度。这个映射函数就做散列函数,存放记录的数组叫做散列表。哈希表的存储是以数据中每个元素的关键字K为自变量,通过散列函数H(k)计算出函数值,以该函数值作为一块连续存储空间的的单元地址,将该元素存储到函数值对应的单元中。
哈希表的存储数据冲突是不同关键字对应相同的存储地址。哈希表查找的时间复杂度为O(1)因为他只与键值有关。
哈希表的装填因子:装填因子 = (哈希表中的记录数)/(哈希表的长度)。装填因子是哈希表装满程度的标记因子。值越大,填入表中的数据元素越多,产生冲突的可能性越大。不同值通过哈希函数产生的键值相同概率就越大。那么为了解决冲突,一方面尽量构造出“完美”的哈希函数使得哈希函数产生的值尽量分布散列,另一方面无法避免的冲突就用相应的算法解决冲突。
哈希函数构造方法有:直接定址法、数字分析法、折叠法 、平方取中法、减去法、基数转换法 、除留余数法、随机乘数法、字符串数值哈希法、旋转法 、伪随机数法。常用的有:1)直接定址法:取关键字或者关键字的某个线性函数值作为哈希地址,即H(Key)=Key或者H(Key)=a*Key+b(a,b为整数),这种散列函数也叫做自身函数。例如:有一个从1到100岁的人口数字统计表,其中,年龄作为关键字,哈希函数取关键字自身。2) 数字分析法:分析一组数据,比如一组员工的出生年月,这时我们发现出生年月的前几位数字一般都相同,因此出现冲突的概率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果利用后面的几位数字来构造散列地址,则冲突的几率则会明显降低,分析数据构造合适的函数。3)平方取中法:取关键字平方后的中间几位作为散列地址,一个数的平方值的中间几位和数的每一位都有关。因此,有平方取中法得到的哈希地址同关键字的每一位都有关,是的哈希地址具有较好的分散性。该方法适用于关键字中的每一位取值都不够分散或者较分散的位数小于哈希地址所需要的位数的情况。4) 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法。例如:每一种西文图书都有一个国际标准图书编号,它是一个10位的十进制数字,若要以它作关键字建立一个哈希表,当馆藏书种类不到10,000时,可采用此法构造一个四位数的哈希函数。5)随机数法:选择一个随机数,去关键字的随机值作为散列地址,通常用于关键字长度不同的场合。6)除留余数法:取关键字被某个不大于哈希表长m的数p除后所得余数为哈希地址,H(key)=key MOD p (p<=m)。
哈希冲突的处理方法有:开放定址法(线性探测法、线性补偿探测法、随机探测法)、拉链法(建立公共溢出区,再散列法)。线性探测是线性再散列法最简单的处理冲突的方法。插入元素时,如果发生冲突,算法会简单的从该位置向后循环遍历hash表,直到找到表中的下一个空位,并将该元素放入该位置中(会导致相同hash值的元素挨在一起和其他hash值对应的位置被占用)。查找元素时,首先散列值所指向的位置,如果没有找到匹配,则继续遍历hash表,直到:1)找到相应的元素;2)找到一个空位,指示查找的元素不存在,(所以不能随便删除元素);(3)整个hash表遍历完毕(指示该元素不存在并且hash表是满的)
2)线性补偿探测法:
拉链法:将所有具有相同哈希地址的而不同关键字的数据元素连接到同一个单链表中。如果选定的哈希表长度为m,则可将哈希表定义为一个有m个头指针组成的指针数组T[0..m-1],凡是哈希地址为i的数据元素,均以节点的形式插入到T[i]为头指针的单链表中。并且新的元素插入到链表的前端,这不仅因为方便,还因为经常发生这样的事实:新近插入的元素最优可能不久又被访问。以下是拉链法优缺点:1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;3)开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;
再散列:当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。常见算法的可视化http://www.comp.nus.edu.sg/~stevenha/visualization/index.html