数据结构之哈希表

来源:互联网 发布:mac粉 编辑:程序博客网 时间:2024/05/22 15:39

       

       小目录

               1.什么是哈希表

           2.哈希表函数的构造方法

            3.处理冲突的方法

            4.哈希表的查找及其分析

             


                1.什么是哈希表

         哈希表是种数据结构,它可以提供快速的插入操作和查找操作。第一次接触哈希表时,它的优点多得让人难以置信。不论哈希表中有多少数据,插入和删除(有时包括侧除)只需要接近常量的时间即0(1)的时间复杂度.那么怎么进行哈希呢?即选取某个函数,依该函数按关键码计算元素的存储位置,并按此存放;查找时,由同一个函数对给定值kx 计算地址,将key与地址单元中元素关键码进行比,确定查找是否成功,这就是哈希方法;哈希方法中使用的转换函数称为哈希函数;按这个思想构造的表称为哈希表。

                     


                 2.哈希表函数的构造方法 

                由于哈希表的构造方法比较多,这里做简单的摘要(除留余数法最常用)。

        一. 直接定址法

        Hash(key)=a·key+b (a、b 为常数)即取关键码的某个线性函数值为哈希地址,这类函数是一一对应函数,不会产生冲突,但要求地址集合与关键码集合大小相同,因此,对于较大的关键码集合不适用。

      二. 除留余数法

     Hash(key)=key mod p (p 是一个整数)即取关键码除以p 的余数作为哈希地址。使用除留余数法,选取合适的p 很重要,若哈希表表长为m,则要求p≤m,且接近m 或等于m。p 一般选取质数,也可以是不包含小于20 质因子的合数。

       三. 乘余取整法

        Hash(key)= ⎣B*(A*key mod 1)⎦ (A、B 均为常数,且0<A<1,B 为整数)以关键码key 乘以A,取其小数部分(A*key mod 1 就是取A*key 的小数部分),之后再用整数B 乘以这个值,取结果的整数部分作为哈希地址。该方法B 取什么值并不关键,但A 的选择却很重要,最佳的选择依赖于关键码集合的特征

       四. 数字分析法

       设关键码集合中,每个关键码均由m 位组成,每位上可能有r 种不同的符号

       五. 平方取中法

       对关键码平方后,按哈希表大小,取中间的若干位作为哈希地址

       六. 折叠法(Folding)

      此方法将关键码自左到右分成位数相等的几部分,最后一部分位数可以短些,然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址。这种方法称为折叠法。


             3.处理冲突的方法

             

      1. 开放寻址法:

         Hi=(H(key) + di) MOD m, i=1,2,…, k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:

     1. di=1,2,3,…, m-1,称线性探测再散列;

     2. di=1^2, (-1)^2, 2^2,(-2)^2, (3)^2, …, ±(k)^2,(k<=m/2)称二次探测再散列;

     3. di=伪随机数序列,称伪随机探测再散列。 ==

  2. 再散列法:

      Hi=RHi(key), i=1,2,…,k RHi均是不同的散列函数,即在同义词产生地址冲突时计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增加了计算时间。

  3. 链地址法(拉链法)

      当存储结构是链表时,多采用拉链法,用拉链法处理冲突的办法是:把具有相同散列地址的关键字(同义词)值放在同一个单链表中,称为同义词链表。有m个散列地址就有m个链表,同时用指针数组T[0..m-1]存放各个链表的头指针,凡是散列地址为i的记录都以结点方式插入到以T[i]为指针的单链表中

     4. 建立一个公共溢出区

     设哈希函数产生的哈希地址集为[0,m-1],则分配两个表:一个基本表ElemType base_tbl[m];每个单元只能存放一个元素;一个溢出表ElemType over_tbl[k];只要关键码对应的哈希地址在基本表上产生冲突,则所有这样的元素一律存入该表中。     


         4.哈希表的查找及其分析

     哈希表的查找过程基本上和造表过程相同。一些关键码可通过哈希函数转换的地址直接找到,另一些关键码在哈希函数得到的地址上产生了冲突,需要按处理冲突的方法进行查找。在介绍的三种处理冲突的方法中,产生冲突后的查找仍然是给定值与关键码进行比较的过程。所以,对哈希表查找效率的量度,依然用平均查找长度来衡量。

    下面给出具体的算法(该算法哈希构造使用除留余数法,冲突采用开放地址法中的线性探测,相关的函数在源码中):

/** @description:在哈希表中查找,主要配合插入操作* @more:在开放地址哈希表中查找关键词,若查找成功,    以p指示待查数据在表中的位置,并且返回查询成功    若查询失败,则返回应该插入位置*/Status SearchHash(HashTable H,KeyType key,int *p, int *c) {    //求哈希地址    *p = Hash(key);    //向插入的位置已经有元素且不等于插入的关键词本身    while(H.elem[*p].key != NULLKEY && H.elem[*p].key != key) {        (*c)++;        if((*c) < m)            //产生冲突,则探寻下一个可能的位置            collision(p,*c);        else            break;    }        //查找成功    if(H.elem[*p].key == key)        return SUCCESS;    else        return UNSUCCESS;}/** @description:求哈希值* @more:可根据实际情况来选择更合适的*/unsigned Hash(KeyType key) {    return key % m;}/** @description:冲突处理函数,这里使用开放地址法*/void collision(int *p,int c) {    *p = (*p + c) % m;}/** @description:哈希表插入元素*/Status InsertHash(HashTable *H,KeyType key) {    int c,p;    //用于计数,也用作步长    c =  0;    if(SearchHash(*H,key,&p,&c))        return DUPLICATE;    /*    产生冲突但是冲突次数没有达到上限    这里的上限是可以调整,根据实际情况来设定    */    else if(c < hashsize[(*H).sizeindex] / 2) {        //插入        (*H).elem[p].key = key;        (*H).count++;        return SUCCESS;    }    //冲突次数超过了上限,故需要重建哈希表即进行扩容    else {        RecreateHash(H);        return UNSUCCESS;    }}/** @description:重建哈希表,即进行扩容*/void RecreateHash(HashTable *H) {    int i,count;    ElemType *p,*elem;    count = (*H).count;    elem = (ElemType *) malloc (count * sizeof(ElemType));    if(!elem)        exit(OVERFLOW);        //将现在哈希表中的元素暂存到elem中    for(i = 0 ; i < m ; i++)         if((*H).elem[i].key != NULLKEY)            *elem = *((*H).elem + i);    (*H).count = 0;    (*H).sizeindex++;    m = hashsize[(*H).sizeindex];    p = (ElemType *) realloc ((*H).elem, m * sizeof(ElemType));    if(!p)        exit(OVERFLOW);    (*H).elem = p;    //初始化新哈希表中元素值    for(i = 0; i < m ; i++)         (*H).elem[i].key = NULLKEY;    //将元素插入到新的哈希表中    for(p = elem ; p < elem + count ; p++)        InsertHash(H,(*p).key);}


    查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素:

  1. 哈希函数是否均匀
  2. 处理冲突的方法
  3. 哈希表的装填因子
哈希表的平均查找长度是装填因子α的函数,只是不同处理冲突的方法有不同的函数。以下给出几种不同处理冲突方法的平均查找长度:


可知,其时间复杂度为O(1)


 蛋疼的上一张测试截图:


附上源码地址:GitHub

      


0 0
原创粉丝点击