• 数据结构与算法系列研究八——链地址法建哈希表


    链地址法建哈希表

    一.实验内容

        建立n元关键字哈希表,用链地址法解决冲突,输出建立的哈希表。(按链表的顺序),输入任意元素实现查找查找结果分成功和失败,要求查找时可以反复输入关键字查找,直至输入停止标识符时程序结束。

    二.输入与输出

      输入:可以用随机数法产生n元关键字,然后,产生哈希表,输入要查找的关键字判断是否存在。
      输出:
    输出哈希表,输出查找的结果。

    三.关键数据结构和核心算法

      关键数据结构:
         链式哈希表由一组数组作为“头指针”,每个数组之后都有一个链表,该链表中存着哈希表的插入元素。因此,可以得到数据结构为:

     1 typedef struct  LNode
     2 {
     3     ElemType data; //节点的数据域,存放要插入节点
     4     struct  LNode *next; //链接指针域
     5 }*LNODE,LNode;//哈希表的表头后带链表结构
     6 typedef  struct  HashNode
     7 {
     8     ElemType  HashData;//头节点,存放该表头号
     9     ElemType  count; //存放该表头连接的节点数
    10     LNode    *first; //链接的第一个指针域
    11 }*HN,HashNode;//哈希表的主体结构
    View Code

       核心算法:

       1.因为哈希表和图的邻接表创建十分相似,故十分简单,只用把数组元素按插入顺序依次插入即可,只要注意关键字的插入只能才出现一次就可以了。具体程序如下:

     1 void  CreateHash(HashNode hash[],int a[],int n,int m)
     2 {//a[]为要插入的节点组,m为哈希表长度,n为数组长度
     3     int i,j;
     4     LNode *p,*q;//链表指针
     5     int flag=0;//标志是否是第一次出现该关键字
     6     for(j=0;j<m;j++)
     7     {
     8         hash[j].HashData = j%m;//标记表头
     9         hash[j].first  = 0; //初始化为空
    10         hash[j].count = 0;  //计数器清零
    11     }
    12     for(i=0;i<n;i++)
    13     {
    14         int k;
    15         k = a[i]%m;  //对a[i]取模
    16         flag=0;
    17         for(j=0;j<m;j++)
    18         {//若模满足待插入条件则插入
    19             for(q=hash[j].first;q;q=q->next)
    20             {
    21                 if(q->data==a[i])
    22                 {
    23                     flag=1; //若出现过则标记
    24                 }
    25             }//end for
    26             if(k==hash[j].HashData&&!flag)
    27             {
    28                 p=(LNode *)malloc(sizeof(LNode));
    29                 p->data=a[i];
    30                 if(!hash[j].count)
    31                 {
    32                     hash[j].first=p;//头节点链接
    33                 }
    34                 else
    35                 {//找到表尾
    36                   for(q=hash[j].first;q->next;q=q->next)
    37                     ;
    38                    q->next=p;//链接
    39                 }
    40                 p->next=NULL;//置空
    41                 hash[j].count++;//计数器加一
    42             }//end if
    43         }//end for
    44     }//end for
    45 }
    View Code

      2.至于查找和输出哈希表就十分简单了,查找到了就输出成功,否则为失败。只要按照hash数组来遍历即可。具体代码如下:

     1 //打印哈希表
     2 void PrintHash(HashNode hash[],int m)
     3 {
     4     int i;
     5     LNode *p;//操作指针
     6     for(i=0;i<m;i++)
     7     {
     8         printf("链表%d有%d个元素,分别为:",i,hash[i].count);
     9         for(p=hash[i].first;p;p=p->next)
    10         {
    11             printf(" %d  ",p->data);//依次输出元素
    12         }
    13         printf("
    ");//输出下一行
    14     }
    15 }
    16 //查找关键字
    17 void SearchKey(HashNode hash[],int m,int key)
    18 {
    19     int i;
    20     int count;
    21     int flag=0;//成败标志
    22     LNode *p;
    23     for(i=0;i<m;i++)
    24     {
    25         count=0;
    26         for(p=hash[i].first;p;p=p->next)
    27         {
    28             count++;
    29             if(p->data==key)
    30             {
    31                 flag=1;//成功置一
    32                 printf("
    成功找到!
    ");   
    33                 printf("位于hash[%d]的第%d个位置
    ",i,count);
    34             }
    35         }
    36     }
    37     if(!flag)
    38     {
    39         printf("
    查找失败!
    ");
    40     }
    41 }
    View Code

    四.理论与测试

      理论:根据哈希表的数据结构,可以构造哈希表,并且可以根据哈希表的结构输出哈希表。
      测试:
    因为采取的是随机数,就省略了输入建表的过程,而查找哈希表的操作及终止条件都已明确。
       截图如下:

    五、附录(源代码)

      1 #include "stdio.h"
      2 #include "stdlib.h"
      3 #include "time.h"   //产生随机数
      4 #define   MAX_SIZE  100//哈希表表头的最大长度
      5 typedef  int ElemType;
      6 typedef struct  LNode
      7 {
      8     ElemType data; //节点的数据域,存放要插入节点
      9     struct  LNode *next; //链接指针域
     10 }*LNODE,LNode;//哈希表的表头后带链表结构
     11 typedef  struct  HashNode
     12 {
     13     ElemType  HashData;//头节点,存放该表头号
     14     ElemType  count; //存放该表头连接的节点数
     15     LNode    *first; //链接的第一个指针域
     16 }*HN,HashNode;//哈希表的主体结构
     17 
     18 void  CreateHash(HashNode hash[],int a[],int n,int m)
     19 {//a[]为要插入的节点组,m为哈希表长度,n为数组长度
     20     int i,j;
     21     LNode *p,*q;//链表指针
     22     int flag=0;//标志是否是第一次出现该关键字
     23     for(j=0;j<m;j++)
     24     {
     25         hash[j].HashData = j%m;//标记表头
     26         hash[j].first  = 0; //初始化为空
     27         hash[j].count = 0;  //计数器清零
     28     }
     29     for(i=0;i<n;i++)
     30     {
     31         int k;
     32         k = a[i]%m;  //对a[i]取模
     33         flag=0;
     34         for(j=0;j<m;j++)
     35         {//若模满足待插入条件则插入
     36             for(q=hash[j].first;q;q=q->next)
     37             {
     38                 if(q->data==a[i])
     39                 {
     40                     flag=1; //若出现过则标记
     41                 }
     42             }//end for
     43             if(k==hash[j].HashData&&!flag)
     44             {
     45                 p=(LNode *)malloc(sizeof(LNode));
     46                 p->data=a[i];
     47                 if(!hash[j].count)
     48                 {
     49                     hash[j].first=p;//头节点链接
     50                 }
     51                 else
     52                 {//找到表尾
     53                   for(q=hash[j].first;q->next;q=q->next)
     54                     ;
     55                    q->next=p;//链接
     56                 }
     57                 p->next=NULL;//置空
     58                 hash[j].count++;//计数器加一
     59             }//end if
     60         }//end for
     61     }//end for
     62 }
     63 //打印哈希表
     64 void PrintHash(HashNode hash[],int m)
     65 {
     66     int i;
     67     LNode *p;//操作指针
     68     for(i=0;i<m;i++)
     69     {
     70         printf("链表%d有%d个元素,分别为:",i,hash[i].count);
     71         for(p=hash[i].first;p;p=p->next)
     72         {
     73             printf(" %d  ",p->data);//依次输出元素
     74         }
     75         printf("
    ");//输出下一行
     76     }
     77 }
     78 //查找关键字
     79 void SearchKey(HashNode hash[],int m,int key)
     80 {
     81     int i;
     82     int count;
     83     int flag=0;//成败标志
     84     LNode *p;
     85     for(i=0;i<m;i++)
     86     {
     87         count=0;
     88         for(p=hash[i].first;p;p=p->next)
     89         {
     90             count++;
     91             if(p->data==key)
     92             {
     93                 flag=1;//成功置一
     94                 printf("
    成功找到!
    ");   
     95                 printf("位于hash[%d]的第%d个位置
    ",i,count);
     96             }
     97         }
     98     }
     99     if(!flag)
    100     {
    101         printf("
    查找失败!
    ");
    102     }
    103 }
    104 
    105 void  MainMenu()
    106 {
    107     int m=16,n=40;
    108     int key,i,j;
    109     HashNode hash[MAX_SIZE];
    110     srand(time(0));
    111     int a[100];
    112     //随机产生小于200的整数
    113     for(i=0;i<n;i++)
    114     {
    115         a[i]=rand()%200;
    116     }
    117     //依次输出产生元素
    118     printf("产生的关键字数组为:
    ");
    119     for(i=0;i<n;i++)
    120     {
    121         printf("%d  ",a[i]);
    122         j++;
    123         if(j%10==0)
    124         {
    125             printf("
    ");
    126         }
    127     }
    128     //创建哈希表
    129     CreateHash(hash,a,n,m);
    130     printf("
    模为%d的哈希表为:
    ",m);
    131     PrintHash(hash,m);//打印哈希表
    132     while(1)
    133     {
    134        printf("请输入关键字,关键字为200时退出:");
    135        scanf("%d",&key);
    136        SearchKey(hash,m,key);
    137        if(key==200)//结束标志,因为200不存在
    138        {
    139            break;
    140        }
    141     }
    142 }  
    143 int main()
    144 {
    145     MainMenu();
    146     return 0;
    147 }                      
    View Code

    六、总结

       Hash函数是非常重要的一种工具,很多算法都需要用到它来解决一些问题,比如信息安全上的MD5算法,视频文件的字幕识别等等,因为Hash函数具有单向性,所以使用起来非常的方便,可以唯一标识一种东西,非常有用。

  • 相关阅读:
    P4396 [AHOI2013]作业 分块+莫队
    B1965 [Ahoi2005]SHUFFLE 洗牌 数论
    B1970 [Ahoi2005]Code 矿藏编码 暴力模拟
    B1968 [Ahoi2005]COMMON 约数研究 数论
    B1237 [SCOI2008]配对 贪心 + dp
    B1108 [POI2007]天然气管道Gaz 贪心
    B1734 [Usaco2005 feb]Aggressive cows 愤怒的牛 二分答案
    B1012 [JSOI2008]最大数maxnumber 分块||RMQ
    HAOI2007 反素数
    NOIP2009 Hankson的趣味题
  • 原文地址:https://www.cnblogs.com/zyrblog/p/6884645.html
Copyright © 2020-2023  润新知