zoukankan      html  css  js  c++  java
  • 散列表

    http://blog.csdn.net/yyxaf/article/details/7527878

    搜索关键词:散列函数、散列表、哈希函数、哈希表、Hash函数、Hash表

    散列方法不同于顺序查找、二分查找、二叉排序树及B-树上的查找。它不以关键字的比较为基本操作,采用直接寻址技术。在理想情况下,无须任何比较就可以找到待查关键字,查找的期望时间为O(1)。

    散列表的概念

    1、散列表

    设所有可能出现的关键字集合记为U(简称全集)。实际发生(即实际存储)的关键字集合记为K(|K|比|U|小得多)。
    散列方法是使用函数h将U映射到表T[0..m-1]的下标上(m=O(|U|))。这样以U中关键字为自变量,以h为函数的运算结果就是相应结点的存储地址。从而达到在O(1)时间内就可完成查找。
    其中:
    ① h:U→{0,1,2,…,m-1} ,通常称h为散列函数(Hash Function)。散列函数h的作用是压缩待处理的下标范围,使待处理的|U|个值减少到m个值,从而降低空间开销。
    ② T为散列表(Hash Table)。
    ③ h(Ki)(Ki∈U)是关键字为Ki结点存储地址(亦称散列值或散列地址)。
    ④ 将结点按其关键字的散列地址存储到散列表中的过程称为散列(Hashing)

    散列表上的运算 

    散列表上的运算有查找、插入和删除。其中主要是查找,这是因为散列表的目的主要是用于快速查找,且插入和删除均要用到查找操作。

    1、散列表类型说明:
    #define NIL -1 //空结点标记依赖于关键字类型,本节假定关键字均为非负整数
    #define M 997 //表长度依赖于应用,但一般应根据。确定m为一素数
    typedef struct{ //散列表结点类型
    KeyType key;
    InfoType otherinfo; //此类依赖于应用
    }NodeType;
    typedef NodeType HashTable[m]; //散列表类型

    2、基于开放地址法的查找算法
    散列表的查找过程和建表过程相似。假设给定的值为K,根据建表时设定的散列函数h,计算出散列地址h(K),若表中该地址单元为空,则查找失败;否则将 该地址中的结点与给定值K比较。若相等则查找成功,否则按建表时设定的处理冲突的方法找下一个地址。如此反复下去,直到某个地址单元为空(查找失败)或者 关键字比较相等(查找成功)为止。

    (1)开放地址法一般形式的函数表示
    int Hash(KeyType k,int i)
    { //求在散列表T[0..m-1]中第i次探查的散列地址hi,0≤i≤m-1
    //下面的h是散列函数。Increment是求增量序列的函数,它依赖于解决冲突的方法
    return(h(K)+Increment(i))%m; //Increment(i)相当于是di
    }
    若散列函数用除余法构造,并假设使用线性探查的开放定址法处理冲突,则上述函数中的h(K)和Increment(i)可定义为:
    int h(KeyType K){ //用除余法求K的散列地址
    return K%m;
    }

    int Increment(int i){//用线性探查法求第i个增量di
    return i; //若用二次探查法,则返回i*i
    }

    (2)通用的开放定址法的散列表查找算法:
    int HashSearch(HashTable T,KeyType K,int *pos)
    { //在散列表T[0..m-1]中查找K,成功时返回1。失败有两种情况:找到一个开放地址
    //时返回0,表满未找到时返回-1。 *pos记录找到K或找到空结点时表中的位置
    int i=0; //记录探查次数
    do{
    *pos=Hash(K,i); //求探查地址hi
    if(T[*pos].key==K) return l; //查找成功返回
    if(T[*pos].key==NIL) return 0;//查找到空结点返回
    }while(++i<m) //最多做m次探查
    return -1; //表满且未找到时,查找失败
    } //HashSearch

    注意:
    上述算法适用于任何开放定址法,只要给出函数Hash中的散列函数h(K)和增量函数Increment(i)即可。但要提高查找效率时,可将确定的散列函数和求增量的方法直接写入算法HashSearch中,相应的算法【参见习题】。

    3、基于开放地址法的插入及建表
    建表时首先要将表中各结点的关键字清空,使其地址为开放的;然后调用插入算法将给定的关键字序列依次插入表中。
    插入算法首先调用查找算法,若在表中找到待插入的关键字或表已满,则插入失败;若在表中找到一个开放地址,则将待插入的结点插入其中,即插入成功。
    void Hashlnsert(HashTable T,NodeTypene w)
    { //将新结点new插入散列表T[0..m-1]中
    int pos,sign;
    sign=HashSearch(T,new.key,&pos); //在表T中查找new的插入位置
    if(!sign) //找到一个开放的地址pos
    T[pos]=new; //插入新结点new,插入成功
    else //插人失败
    if(sign>0)
    printf("duplicate key!"); //重复的关键字
    else //sign<0
    Error("hashtableoverflow!"); //表满错误,终止程序执行
    } //Hashlnsert

    void CreateHashTable(HashTable T,NodeType A[],int n)
    { //根据A[0..n-1]中结点建立散列表T[0..m-1]
    int i
    if(n>m) //用开放定址法处理冲突时,装填因子α须不大于1
    Error("Load factor>1");
    for(i=0;i<m;i++)
    T[i].key=NIL; //将各关键字清空,使地址i为开放地址
    for(i=0;i<n;i++) //依次将A[0..n-1]插入到散列表T[0..m-1]中
    Hashlnsert(T,A[i]);
    } //CreateHashTable

    4、删除
    基于开放定址法的散列表不宜执行散列表的删除操作。若必须在散列表中删除结点,则不能将被删结点的关键字置为NIL,而应该将其置为特定的标记DELETED。
    因此须对查找操作做相应的修改,使之探查到此标记时继续探查下去。同时也要修改插人操作,使其探查到DELETED标记时,将相应的表单元视为一个空单元,将新结点插入其中。这样做无疑增加了时间开销,并且查找时间不再依赖于装填因子。
    因此,当必须对散列表做删除结点的操作时,一般是用拉链法来解决冲突。
    注意:
    用拉链法处理冲突时的有关散列表上的算法【参见练习】。

    5、性能分析
    插入和删除的时间均取决于查找,故下面只分析查找操作的时间性能。
    虽然散列表在关键字和存储位置之间建立了对应关系,理想情况是无须关键字的比较就可找到待查关键字。但是由于冲突的存在,散列表的查找过程仍是一个和关键字比较的过程,不过散列表的平均查找长度比顺序查找、二分查找等完全依赖于关键字比较的查找要小得多。

    (1)查找成功的ASL
    散列表上的查找优于顺序查找和二分查找。
    【例】在例9.1和例9.2的散列表中,在结点的查找概率相等的假设下,线性探查法和拉链法查找成功的平均查找长度分别为:
    ASL=(1×6+2×2+3×l+9×1)/10=2.2 //线性探查法
    ASL=(1×7+2×2+3×1)/10=1.4 //拉链法
    而当n=10时,顺序查找和二分查找的平均查找长度(成功时)分别为:
    ASL=(10+1)/2=5.5 //顺序查找
    ASL=(1×l+2×2+3×4+4×3)/10=2.9 //二分查找,可由判定树求出该值

    (2) 查找不成功的ASL
    对于不成功的查找,顺序查找和二分查找所需进行的关键字比较次数仅取决于表长,而散列查找所需进行的关键字比较次数和待查结点有关。因此,在等概率情况下,也可将散列表在查找不成功时的平均查找长度,定义为查找不成功时对关键字需要执行的平均比较次数。
    【例】例9.1和例9.2的散列表中,在等概率情况下,查找不成功时的线性探查法和拉链法的平均查找长度分别为:
    ASLunsucc=(9+8+7+6+5+4+3+2+1+1+2+1+10)/13=59/13≈4.54
    ASLunsucc=(1+0+2+1+0+1+1+0+0+0+1+0+3)/13≈10/13≈0.77
    注意:
    ①由同一个散列函数、不同的解决冲突方法构造的散列表,其平均查找长度是不相同的。
    ②散列表的平均查找长度不是结点个数n的函数,而是装填因子α的函数。因此在设计散列表时可选择α以控制散列表的平均查找长度。
    ③ α的取值
    α越小,产生冲突的机会就小,但α过小,空间的浪费就过多。只要α选择合适,散列表上的平均查找长度就是一个常数,即散列表上查找的平均时间为O(1)。
    ④ 散列法与其他查找方法的区别
    除散列法外,其他查找方法有共同特征为:均是建立在比较关键字的基础上。其中顺序查找是对无序集合的查找,每次关键字的比较结果为"="或"!="两种可 能,其平均时间为O(n);其余的查找均是对有序集合的查找,每次关键字的比较有"="、"<"和">"三种可能,且每次比较后均能缩小下次 的查找范围,故查找速度更快,其平均时间为O(lgn)。而散列法是根据关键字直接求出地址的查找方法,其查找的期望时间为O(1)。

  • 相关阅读:
    [计算机故障处理]小区宽带无法上网(中国联通)
    [计算机故障]笔记本无法启动,开机启动出现“Operating System Not Found”无法进系统
    [英语学习]微软面试前英语集训笔记-day 1
    [办公应用]如何将excel合并单元格分拆后每个单元格上仍保留数据?
    [办公应用]如何设置IE打印时的默认页边距,并设置纸张为横向(会计票据打印)
    [计算机故障]excel无法存盘,总是自动重启恢复
    codeforces732D 二分加贪心。。
    topcoder-srm701-div2-900 博弈计算二进制位1的个数dp状态压缩
    什么是超级立方体,HyperCube
    从维度理解dp问题
  • 原文地址:https://www.cnblogs.com/zsboy/p/2733278.html
Copyright © 2011-2022 走看看