链地址法建哈希表
一.实验内容
建立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