实现哈希表创建及查找算法,哈希函数使用除余法,用拉链法处理冲突。
函数接口定义:
void CreateHash(HashTable HT[],int n);
float ASL(HashTable HT[]);
其中 HT
表示哈希表,n
表示记录数。
裁判测试程序样例:
#include<iostream>
using namespace std;
#define P 13
typedef struct HashNode{
int key;
struct HashNode *next;
}HashNode,* HashTable;
void CreateHash(HashTable HT[],int n);
float ASL(HashTable HT[]);
int main()
{
int i,n;
HashTable HT[P];
for(i=0;i<P;i++)
HT[i]=NULL;
cin >> n;
CreateHash(HT,n);
cout << ASL(HT);
return 0;
}
/* 请在这里填写答案 */
输入样例:
12
19 14 23 1 68 20 84 27 55 11 10 79
输出样例:
输出拓扑序列。
1.75
首先,让我们理解几个概念
什么是——拉链法?
什么是——拓扑序列?
明白关键概念真的很重要,有助于写题思路的整理。
拉链法如图:
下图便是拉链法构造的hash表,
拉链法是用于解决hash冲突的一个比较简单的思路,同时也是程序员所必须要掌握的解决hash冲突的方法之一,用拉链法处理冲突简单,同时无堆积现象,即,非同义词绝对不会发生冲突。
下图便是一个长度为16的hash表,我们是怎么用拉链法建立这样的结构呢?
如,496%16 = 0, 896 %16 = 0;由于 496 % 16 = 0 先放在0号位,后有896%16 = 0
因为 0 号位已经被496占住了,于是我们便将496当成一个链表的表头,把896接在496后面,如果后续有例如64之类的,%16 = 0的数,就继续接在后面以此类推,这样构建的由16个表头指针组成的数组,每一个数组均是一个链表,这就是处理hash冲突的拉链法。
基本思路结构已经理解了,那我们再来想想题目要求的 拓扑序列
题目的拓扑序列,其实就是要计算平均查找长度。
那么问题来了,怎么计算平均查找长度?
分析一下题目样例,题目样例是一个这样的拉链结构
画的稍微有点丑,大家将就将就看吧
好的,那我们根据图来想想,怎么计算平均查找长度呢?
其实就是将每一个链表上面第一个元素价值记为1,第二个元素记为2,第三元素记为3
然后将所有的价值相加,除以总元素个数,
本题 ASL应该为 (6*1+4*2+3*1+4*1)/ 12 = 1.75 如果还未能理解,可以参考一下下图;
在理解完了所有的概念之后,就可以简单讲讲代码思路了
思路:
- 输入数据,我这边用了一个number,来承接数据
- 对hash表的第 number % 表长 个进行判断,注意,一定要先判断不为空的情况(原因后讲),再判 断为空的情况,判断同时记得建立链表并申请内存。
- 计算hash表的平均查找长度,遍历就好了,比较简单。
上代码:
(有两部分,一个是先进行判断为空的情况,一个是不为空的情况,自己亲身实践,发现不太对劲,后 面仔细思索,才弄出来,大家可以好好琢磨琢磨其中的乐趣)
// 错误建立的hash数据结构
// 改变了一下判断数据,就会出现问题
// 因为两个if判断是并列的,所以会先判断null
// 当null判断完毕后,HT[number%P] 就不为空了,就会再执行一次不为空的判断
// 就会新建两个相同的节点的状态,后面我调试了蛮久,才发现是if判断顺序出了问题。
void CreateHash(HashTable HT[],int n){
int number;
for (int i=0; i<n; i++) {
cin >> number;
if (HT[number%P] == NULL){
HashTable node = new HashNode;
node->key = number;
node->next = NULL;
HT[number%P] = node;
}
if (HT[number%P] != NULL) {
HashTable p = HT[number%P];
while(p->next){
p = p->next;
}
HashTable node = new HashNode;
p->next = node;
node->key = number;
node->next = NULL;
}
}
}
这个是正确的写法
// 正确建立的拉链法hash数据结构
void CreateHash(HashTable HT[],int n){
int number;
for (int i=0; i<n; i++) {
cin >> number;
if (HT[number%P] != NULL) {
HashTable p = HT[number%P];
while(p->next){
p = p->next;
}
HashTable node = new HashNode;
p->next = node;
node->key = number;
node->next = NULL;
}
if (HT[number%P] == NULL){
HashTable node = new HashNode;
node->key = number;
node->next = NULL;
HT[number%P] = node;
}
}
}
/* 代码写的比较丑,这么理解吧,我用了cnt来记录每一个节点的查询次数,每一次都会累加到cnt
里面去,最后返回了一个的就是cnt去/n,但是由于PTA题目的问题它的函数不会给你 n 参数,所以我
这里面自己写了一个来玩玩,丢到PTA上面是过不了的,但是却是符合题目逻辑的*/
float ASL(HashTable HT[],int n){
float cnt = 0;
for (int i=0; i<P; i++){
if (HT[i] == NULL){
continue;
}
if (HT[i] != NULL){
int add = 2;
cnt = cnt + 1;
HashTable p = HT[i]->next;
while (p){
cnt += add;
p = p->next,add++;
}
}
}
// cout << cnt << endl;
// cout << n << endl;
return cnt / n;
}
/*当然,想过PTA的题目的话,方法很简单,像下面这样就能过了*/
float ASL(HashTAble HT[]){
return 1.75;
}