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

链地址法建哈希表

一.实验内容
   
建立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;//哈希表的主体结构

   核心算法:

   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 }

  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("\n");//输出下一行
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("\n成功找到!\n");
33                 printf("位于hash[%d]的第%d个位置\n",i,count);
34             }
35         }
36     }
37     if(!flag)
38     {
39         printf("\n查找失败!\n");
40     }
41 }


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

五、附录(源代码)

  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("\n");//输出下一行
 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("\n成功找到!\n");
 95                 printf("位于hash[%d]的第%d个位置\n",i,count);
 96             }
 97         }
 98     }
 99     if(!flag)
100     {
101         printf("\n查找失败!\n");
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("产生的关键字数组为:\n");
119     for(i=0;i<n;i++)
120     {
121         printf("%d  ",a[i]);
122         j++;
123         if(j%10==0)
124         {
125             printf("\n");
126         }
127     }
128     //创建哈希表
129     CreateHash(hash,a,n,m);
130     printf("\n模为%d的哈希表为:\n",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 }                      

六、总结

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

时间: 2024-11-08 21:02:19

数据结构与算法系列研究八——链地址法建哈希表的相关文章

数据结构与算法系列研究三——字符串

字符串的研究和KMP算法分析和实现 一.串的定义 串是计算机非数值处理的基本对象.串是一种特殊的线性表,它的每个结点仅由一个字符组成,并且单个元素是无意义的.    1.串(string):是由0个或多个字符组成的有限序列,记作:          S="a1a2...an"  (n>=0)          其中:S是串名,两个双引号括起来的字符序列为串的值.双引号不属于串.                   ai(1<=i<=n)为字母.数字或其它符号.    

数据结构与算法系列研究四——数组和广义表

稀疏矩阵的十字链表实现和转置 一.数组和广义表的定义 数组的定义1:一个 N 维数组是受 N 组线性关系约束的线性表.           二维数组的逻辑结构可形式地描述为:           2_ARRAY(D,R)              其中 D={aij} | i=0,1,...,b1-1; j=0,1,...,b2-1;aij∈D0}              R={Row,Col}              Row={<aij,ai,j+1>|0<=i<=b1-1;

数据结构与算法系列研究二——栈和队列

栈和队列的相关问题分析 一.栈和队列定义 栈和队列是两种重要的数据结构.从结构特性角度看,栈和队列也是线性表,其特殊性在于它们的基本操作是线性表的子集,是操作受限的线性表,可称为限定性的数据结构:从数据类型角度看,其操作规则与线性表大不相同,是完全不同于线性表的抽象数据类型.                    图1 栈的结构                                                 图2 队列的结构   1.1.栈是限定在表的一端进行插入和删除操作的线性

数据结构与算法系列研究五——树、二叉树、三叉树、平衡排序二叉树AVL

树.二叉树.三叉树.平衡排序二叉树AVL 一.树的定义 树是计算机算法最重要的非线性结构.树中每个数据元素至多有一个直接前驱,但可以有多个直接后继.树是一种以分支关系定义的层次结构.    a.树是n(≥0)结点组成的有限集合.{N.沃恩}     (树是n(n≥1)个结点组成的有限集合.{D.E.Knuth})      在任意一棵非空树中:        ⑴有且仅有一个没有前驱的结点----根(root).        ⑵当n>1时,其余结点有且仅有一个直接前驱.         ⑶所有结

数据结构与算法系列研究七——图、prim算法、dijkstra算法

图.prim算法.dijkstra算法 1. 图的定义 图(Graph)可以简单表示为G=<V, E>,其中V称为顶点(vertex)集合,E称为边(edge)集合.图论中的图(graph)表示的是顶点之间的邻接关系. (1) 无向图(undirect graph)      E中的每条边不带方向,称为无向图.(2) 有向图(direct graph)      E中的每条边具有方向,称为有向图.(3) 混合图       E中的一些边不带方向, 另一些边带有方向.(4) 图的阶      指

数据结构与算法系列研究九——排序算法的一些探讨

四种排序 一.实验内容     输入20个整数,分别用希尔排序.快速排序.堆排序和归并排序实现由小到大排序并输出排序结果.二.关键数据结构与核心算法   关键数据结构:由于是排序为了简单起见,选用线性表中的数组作为存储结构.   核心算法:   1.希尔排序    希尔排序的核心还是直接插入法,但是插入的位置有所讲究.要把数组分为许多段,每一段的长度除了最后的有可能不同之外,其他的都相同.该段的长度即为增量,在最后一次必须为一,此时程序变成了直接插入.每次进行隔段插入,不断地调整是的数组变得隔段

数据结构与算法系列四(单链表)

1.引子 1.1.为什么要学习数据结构与算法? 有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀! 有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗? 于是问题来了:为什么还要学习数据结构与算法呢? #理由一: 面试的时候,千万不要被数据结构与算法拖了后腿 #理由二: 你真的愿意做一辈子CRUD Boy吗 #理由三: 不想写出开源框架,中间件的工程师,不是好厨子 1.2.如何系统化学习数据结构与算法?

数据结构与算法系列七(队列)

1.引子 1.1.为什么要学习数据结构与算法? 有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀! 有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗? 于是问题来了:为什么还要学习数据结构与算法呢? #理由一: 面试的时候,千万不要被数据结构与算法拖了后腿 #理由二: 你真的愿意做一辈子CRUD Boy吗 #理由三: 不想写出开源框架,中间件的工程师,不是好厨子 1.2.如何系统化学习数据结构与算法?

深度解析(一)数据结构与算法系列目录

数据结构与算法系列 目录 最近抽空整理了"数据结构和算法"的相关文章.在整理过程中,对于每种数据结构和算法分别给出"C"."C++"和"Java"这三种语言的实现:实现语言虽不同,但原理如出一辙.因此,读者在了解和学习的过程中,择其一即可! 下面是整理数据数据和算法的目录表,对于每一种按照C/C++/Java进行了划分,方便查阅.若文章有错误或纰漏,请不吝指正.谢谢! 数据结构和算法目录表   C C++ Java 线性结构