`

哈希表

 
阅读更多
8、设哈希表长 m=14,哈希函数 H(key)=key%11。表中已有 4 个结点:

addr(15)=4

addr(38)=5

addr(61)=6

addr(84)=7
其余地址为空,如用二次探测再散列处理冲突,关键字为 49的结点的地址是( ) 。
a)  8
b)  3 
c)  5 
d)  9

结果 为9  :根据下面所说二次探测: 3^2



1、序

        该篇分别讲了散列表的引出、散列函数的设计、处理冲突的方法。并给出一段简单的示例代码。
2、散列表的引出

        给定一个关键字集合U={0,1......m-1},总共有不大于m个元素。如果m不是很大,我们可以定义一个数组T[0...(m-1)],把U映射到数组T上,每个位置对应U中的一个关键字,若U中没有关键字为k的元素,则T[k]=NULL。我们称T为直接寻址表,不管是插入、删除、查找,只需o(1)的时间。但是注意前提,当”m不是很大的时候“。显然这个前提限制性很大,m很大时,必然会浪费很多空间,那该怎么办呢?于是就有了散列表:给定n个元素、m个存放位置(也称槽位),通过散列函数把关键字和存储位置关联起来,使每一个关键字与结构中一个唯一的存储位置相对应。于是在查找时,根据散列函数查找关键字的位置,不需要比较就可以取得要查找的记录。散列表也称哈希表。
3、散列函数

      散列函数有很多,好的散列函数特点是:(近似的)满足简单一致散列,即对于关键字集合U中的任何一个关键字,经散列函数映射到地址集合中任何一个地址的概率是相等的,此时可以称为均匀散列函数,也就是说使关键字经过散列函数得到一个“随机的地址”,以便使一组关键字的散列地址均匀分布在整个地址区间,减少冲突。很多时候我们将关键字解释为自然数。下面给出几种常用的散列函数。

       (1) 除法散列法

     散列函数:h(key)=key%p,其中p的取值很重要,这个函数得出的散列地址值不会超过p,同时可以选作p的值常常是与2的整数幂不太接近的质数。当存放位置m较大时,p不宜过小。

     (2)乘法散列法

     散列函数h(key)=[p*(key*A-(int)key*A)].其中0<A<1.(key*A-(int)key*A)是取key*A得小数部分,最后再乘上常数p,最后的值向下取整。一般p选择2的某个幂次,对p的选择并没有什么特别的要求。

     (3)全域散列

     在执行开始时,从一族仔细设计的函数中,随机地选择一个作为散列函数。这里的随机选择针对的是一次对散列表的应用,而不是一次简单的插入或查找操作。散列函数的确定性,是查找操作正确执行的保证。全域散列法确保,当key1 != key2时,两者发生碰撞的概率不大于1/m。设计一个全域散列函数类的方法如下,该方法中,散列表大小m的大小是任意的。

     全域散列函数类类设计方法:选择一个足够大的质数p,使得每一个可能的关键字都落在0到p-1的范围内。设Zp表示集合{0, 1, …, p-1},Zp*表示集合{1, 2, …, p-1}。对于任何a∈Zp*和任何b∈Zp,定义散列函数ha,b (k)= ((ak+b) mod p) mod m所有这样的散列函数构成的函数族为:Hp,m = {ha,b : a∈Zp*和b∈Zp}由于对a来说有p-1种选择,对于b来说有p种选择,因而,Hp,m中共有p(p-1)个散列函数。在一次散列表应用中,a、b是随机生成的在一定范围的数。举个例子:若p=17,m=6,此次散列应用中随机生成a=3,b=4.则h3,4(8)=5.
4、处理冲突的方法

       当h(key1)==h(key2)时,两个不同的关键字对应得哈希地址相同,于是就产生了冲突,好的哈希函数只能避免冲突,不能完全消除冲突。那么该怎么样处理冲突呢?下面给出几种常用的方法。

     (1)开放寻址法

     在开发寻址法法中,所有的元素都存放在散列表里。插入一个元素时,可以连续地检查散列表的各项,直到找到一个空槽来放置待插入的关键字为止。对开发地址法来说,要求对每一个关键字k,探查序列必须是(0,1...m-1)的一个排列,即能够探测所有的槽。

     H=(H(key)+d) mod m 其中m表示哈希表长度,d为增量序列,H(key)为哈希函数

     线性探测再散列:当上式中d取值依次为1,2,3...m-1时,称为线性探测再散列。这种方法中,初始探查的位置确定了整个探测序列,比如第一个探测位置T[1],那么下一个位置是T[2],然后T[3]....故只有m种不同的探测序列。随着时间推移,连续被占用的槽不断增多,平均查找时间随之增加。这种现象称为集群现象。

    二次探测再散列:d=1^2,(-1)^2,2^2,(-2)^2......这时就是二次探测再散列,初始探查的位置确定了整个探测序列,故只有m种不同的探测序列。但出现集群现象的概率降低了很多。

    伪随机探测再散列:d=伪随机数序列。

    双重散列:使用的函数:h(k,i) = (h1(k) + i h2(k)) mod m, i=0, 1, …, m-1

    为能查找整个散列表,值h2(k)要与表的大小m互质。确保这一条件成立的一种方法是取m为2的幂,并设计一个总能产生奇数的h2。另一种方法是取m为质数,并设计一个总是产生较m小的正整数的h2。例如,可以取m为质数,h1(k)=k mod m , h2(k)=1+(k mod m’),m’=m-1。

     (2)链地址法

     把散列到同一个槽中的所有元素都放在一个链表中。相对于开放地址法,可能会增加存储空间。

     (3)建立一个公共溢出区

     若发生冲突,把key存入公共溢出区。
5、完全散列

      如果某一种散列技术在进行查找时,其最坏情况内存访问次数为O(1)的话(没有冲突产生),则称其为完全散列(perfect hashing)。通常利用一种两级的散列方案,每一级上都采用全域散列,用一个二次散列表Sj存储所有散列到槽j中的关键字,就像是把链接法中的链表改成一个散列表。为了确保在第二级上不出现碰撞,需要让第二级散列表Sj的大小mj为散列到槽j中的关键字数nj的平方。如果利用从某一全域散列函数类中随机选出的散列函数h,来将n个关键字存储到一个大小为m=n的散列表中,并将每个二次散列表的大小置为mj=nj2 (j=0, 1, …, m-1),则在一个完全散列方案中,存储所有二次散列表所需的存储总量的期望值小于2n。
6、散列表性能分析

       装填因子a=表中填入的记录数/散列表的长度。a标志着散列表的装满程度。散列表查找成功和不成功的平均查找长度分析很复杂,其中链接法处理冲突插入和删除时间为o(1),操作也很方便,适合经常有记录删除的哈希表。链接法对哈希函数的依赖很大,如果哈希函数不好,可能会浪费很多空间。而开放地址法的删除记录时可以把删除的位置赋一个特殊值以标识这个记录被删除了。这样就不会影响其他记录插入和查找。
7、附录

       参考书籍:《算法导论》  《数据结构》

       哈希表的应用实例:

    [cpp] view plaincopy

        /*
         * 题目:给定一个全部由字符串组成的字典,字符串全部由大写字母构成。其中为每个字符串编写密码,编写的
         *       方式是对于 n 位字符串,给定一个 n 位数,大写字母与数字的对应方式按照电话键盘的方式:
         *         2: A,B,C     5: J,K,L    8: T,U,V
         *         3: D,E,F     6: M,N,O    9: W,X,Y,Z
         *         4: G,H,I     7: P,Q,R,S
         * 题目给出一个1--12位的数,找出在字典中出现且密码是这个数的所有字符串。字典中字符串的个数不超过5000。
         *        
         * 思路:1.回溯法找出所有可能的字符串
         *       2.在字典中查找此字符串是否存在。(字典存储采用哈希表存储) 
         *
         */ 
         
        #include<stdio.h> 
        #include<stdlib.h> 
        #include<string.h> 
         
        #define HASHTABLE_LENGTH 5001  //哈希表长度 
        #define STRING_LENGTH   13     //单词最大长度 
         
        //字符串 
        typedef struct 
        { 
            char str[STRING_LENGTH]; 
            int length; 
        }HString; 
         
        HString string={'\0',0};             //暂存可能的字符串 
        HString hashTable[HASHTABLE_LENGTH]; //哈希表 
         
        //hash函数,构造哈希表 
        void createHashTable(char *str) 
        { 
            int i,key,step=1; 
            i=key=0; 
            while(str[i]){ 
                key+=str[i++]-'A'; 
            } 
            key%=HASHTABLE_LENGTH; 
            while(1){ 
                if(hashTable[key].length==0){ 
                    hashTable[key].length=strlen(str); 
                    strcpy(hashTable[key].str,str); 
                    break; 
                } 
                key=(key+step+HASHTABLE_LENGTH)%HASHTABLE_LENGTH; 
                //处理冲突,线性探测再散列 
                if(step>0) 
                    step=-step; 
                else{ 
                    step=-step; 
                    step++; 
                } 
            } 
        } 
         
        //从文件中读字典 
        void readString() 
        { 
            int i; 
            char str[STRING_LENGTH]; 
            char ch; 
            FILE *fp; 
            if((fp=fopen("document/dictionary.txt","r"))==NULL){    
               printf("can not open file!\n");    
               exit(0);    
            }   
             
            i=0; 
            while((ch=getc(fp))!=EOF){    
                if(ch=='\n'){//读完一个字符串 
                    str[i]='\0'; 
                    createHashTable(str); 
                    i=0; 
                    continue; 
                } 
                str[i++]=ch; 
            } 
         
            if(fclose(fp)){    
                printf("can not close file!\n");    
                exit(0);    
            }    
        } 
         
        //在哈希表中查找是否存在该字符串,存在返回1,不存在返回0 
        int search(char *str) 
        { 
            int i,key,step=1; 
            i=key=0; 
            while(str[i]){ 
                key+=str[i++]-'A'; 
            } 
            key%=HASHTABLE_LENGTH; 
            while(1){ 
                if(hashTable[key].length==0) 
                    return 0; 
                if(strcmp(hashTable[key].str,str)==0){ 
                    return 1; 
                } 
                key=(key+step+HASHTABLE_LENGTH)%HASHTABLE_LENGTH; 
                //处理冲突,线性探测再散列 
                if(step>0) 
                    step=-step; 
                else{ 
                    step=-step; 
                    step++; 
                } 
            } 
            return 0; 
        } 
         
        //求所有可能的字符串 
        void getString(char* num) 
        { 
            int i,digit,max; 
            if(*num==0){//递归出口,字符串已到末尾 
                string.str[string.length]='\0'; 
                if(search(string.str))//这个字符串存在于字典中,输出 
                    puts(string.str); 
                return; 
            } 
         
            digit=*num-'0';//取第一位字符,转成数字 
            if(digit>=2&&digit<=6){ 
                i=(digit-2)*3+'A'; 
                max=(digit-2)*3+'A'+3; 
            } 
            else if(digit==7){ 
                i='P'; 
                max='P'+4; 
            } 
            else if(digit==8){ 
                i='T'; 
                max='T'+3; 
            } 
            else if(digit==9){ 
                i='W'; 
                max='W'+4; 
            } 
         
            for(i;i<max;i++){ 
                string.str[string.length++]=i; 
                getString(num+1); //递归 
                string.length--; 
            } 
        } 
         
        void main() 
        { 
            char num[STRING_LENGTH];   //由于输入的数字超出了unsigned long的范围,所以用字符串来存储 
            readString();              //把字典从文件中读入内存 
            printf("please inputer an number(1--12位,不能有0或1)\n"); 
            scanf("%s",num); 
            getString(num); 
        } 
分享到:
评论

相关推荐

    哈希表课程设计数据结构实验报告——哈希表设计

    哈希表课程设计数据结构实验报告——哈希表设计 针对某个集体中的人名设计一个哈希表,使得平均查找长度不超过R,完成相应的建立和查表程序. 1.2 人名为汉语拼音形式,最长不超过18个字符(如:庄双双 ...

    哈希表设计 哈希表 哈希表

    哈希表是一种高效的数据结构,它通过特定的函数——哈希函数,将任意大小的键(key)映射到一个固定大小的数组中,从而实现快速的查找、插入和删除操作。这种数据结构的设计旨在解决在大量数据中查找特定元素的问题...

    哈希表设计 针对某个集体(比如你所在的班级)中的“人名”设计一个哈希表,使得平均查找长度不超过R,完成相应的建表和查表程序。

    第二步:实现哈希表的基本操作,包括创建哈希表、销毁哈希表、查找哈希表、插入哈希表等。 第三步:实现哈希函数,使用除留余数法构造哈希函数,并使用伪随机探测再散列法处理冲突。 第四步:实现查找算法,使用...

    数据结构哈希表设计实验报告

    哈希表是一种高效的数据结构,它通过特定的函数——哈希函数,将任意大小的键(key)映射到一个固定大小的数组中,从而实现快速查找、插入和删除操作。在“数据结构哈希表设计实验报告”中,我们可能会涉及到以下几...

    哈希表操作(c语言版)

    ////采用除留余数法定义哈希表,哈希表长度为10,哈希函数为H(key)=key%13。产生冲突时采用线性探测法实现下面要求的功能。 ////(1)初始化哈希表,置空哈希表 ////(2)在哈希表中查找元素 ////(3)在哈希表中...

    哈希表相关操作实现

    哈希表,也被称为散列表,是计算机科学中一种非常重要的数据结构,它提供了一种高效的数据存储和检索方法。哈希表通过将键(Key)映射到一个索引位置来实现快速访问,这个索引位置是通过哈希函数计算得出的。哈希...

    hashtab2_C语言_哈希表删除、添加、寻找_codeblocks_

    哈希表是一种高效的数据结构,它通过特定的函数(哈希函数)将数据映射到一个固定大小的数组中,以此实现快速的查找、插入和删除操作。在C语言中,我们可以手动构建哈希表来处理这些操作。Code::Blocks是一款流行的...

    哈希表的建立和查找哈希表的建立和查找哈希表的建立和查找

    哈希表是一种高效的数据结构,它通过特定的函数——哈希函数,将数据映射到一个固定大小的数组中,以此实现快速的插入、查找和删除操作。在本主题中,我们将深入探讨哈希表的建立和查找过程,以及相关的算法和设计...

    哈希表的设计与实现C语言

    哈希表是一种高效的数据结构,它通过特定的算法——哈希函数,将任意大小的键(key)映射到一个固定大小的数组中,从而实现快速查找、插入和删除操作。在C语言中,实现哈希表需要理解其基本原理,并掌握如何利用...

    数据结构哈希表实验报告

    数据结构中的哈希表是一种高效的数据存储和检索结构,它通过特定的哈希函数将关键字映射到数组的索引位置,实现快速访问。在这个实验报告中,我们关注的是如何构建哈希表并进行基本操作,包括插入、删除、查找等。 ...

    哈希表设计程序设计+数据结构实验报告

    哈希表设计程序设计+数据结构实验报告 1.1 针对某个集体中的人名设计一个哈希表,使得平均查找长度不超过R,完成相应的建立和查表程序. 1.2 人名为汉语拼音形式,最长不超过18个字符(如:庄双双 zhuangshuangshuang)...

    哈希表算法 链地址法解决冲突

    哈希表是一种高效的数据结构,它通过特定的哈希函数将键(key)映射到一个固定大小的数组中,以此实现快速的插入、查找和删除操作。在本例中,我们关注的是如何利用链地址法来处理哈希冲突。 哈希函数是哈希表的...

    大数据结构课程设计--哈希表实验报告材料

    "大数据结构课程设计--哈希表实验报告材料" 在大数据结构课程设计中,哈希表实验报告材料是非常重要的一部分。本文档将详细介绍哈希表的设计和实现,包括哈希函数的构造、冲突处理、查找算法等。 一、哈希表的设计...

    哈希表应用 设计哈希表实现图书查找系统,完成相应的建表和查表程序。c语言课设

    哈希表应用 设计哈希表实现图书查找系统,完成相应的建表和查表程序。从键盘输入各图书相关信息,分别以图书编号为关键字建立散列表。待填入哈希表的书号至少30个;构造合适的哈希函数。 (1)记录由外部输入。 (2...

    姓名哈希表创建哈希表,将ASCII码取余得KEY值,若未发生冲突存入哈希表

    /为班级30个人的姓名设计一个哈希表,假设姓名用汉语拼音表示。要求用除留余数法 构造哈希函数,用线性探测再散列法处理冲突,平均查找长度的上限为2。 编写数据结构和算法来实现。要求:将哈希函数和处理冲突方法...

    c + + 哈希表实现数字排序

    哈希表是一种在计算机科学中广泛使用的数据结构,它的主要目的是快速查找、插入和删除元素。在这个特定的C++程序中,哈希表被用来实现一个数字排序算法,特别是针对大整数范围内的数据。程序的目标是处理多组测试...

    哈希表源代码-哈希表模型

    在这个"哈希表源代码"压缩包中,我们可以期待找到实现哈希表的源代码,这对于理解哈希表的工作原理以及在实际编程中应用哈希表非常有帮助。 哈希表的基本概念: 1. 键值对:哈希表由一系列键值对组成,每个键对应...

    哈希表的实现

    哈希表是一种高效的数据结构,它通过特定的算法——哈希函数,将任意大小的键(key)映射到一个固定大小的数组中,从而实现快速查找、插入和删除操作。在本项目中,我们将深入探讨哈希表如何用于管理用户名和密码。 ...

    哈希表的设计与实现【课程设计】

    哈希表的设计与实现课程设计 问题描述:针对某个单位电话号码簿,设计一个哈希表,并完成相应的建表和查表程序。 基本要求:设每个记录有下列数据项:电话号码、用户名、住址。从键盘输入各记录,以用户名为关键字...

Global site tag (gtag.js) - Google Analytics