hnust 1968 链地址法的哈希表
实现链地址法的哈希表,表的构造参见教材图7.30.
拷贝下面的代码,实现InsertHashTable、SearchHashTable和DeleteHashTable三个函数。
//链地址法的散列表
#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;
typedef struct node {
int key;
struct node *next;
} Node, *PNode;
typedef struct {
PNode *elem;
int sz; //size of hashtable
} HashTable;
void InitHashTable(HashTable &ht, int n)
{
ht.sz = n;
//申请大小为n的指针数组,每个指针初始化为NULL
ht.elem = new PNode[n]();
}
//将k插入ht中对应的链表尾部,如果表中k已经存在,直接返回false,否则插入k后返回true
bool InsertHashTable(HashTable &ht, int k)
{
/****在此下面完成代码************/
/***********************************/
}
//如果表中存在k,返回指向对应结点的指针,否则返回NULL
PNode SearchHashTable(HashTable ht, int k)
{
/****在此下面完成代码************/
/********************************/
}
//删除表中韩关键字k的结点并返回true。如果不存在,直接返回false。
bool DeleteHashTable(HashTable &ht, int k)
{
/****在此下面完成代码************/
/***********************************/
}
static void PrintLinklist(PNode h)
{
for(PNode p = h; p; p = p->next) {
cout << "->" << p->key;
}
cout << endl;
}
void PrintHashTable(HashTable ht)
{
cout << "Print the hash table\n";
for(int i = 0; i < ht.sz; i++) {
cout << '[' << i << ']';
PrintLinklist(ht.elem[i]);
}
}
void DestroyHashTable(HashTable &ht)
{
//You should do this!
}
int main()
{
int n;
HashTable ht;
string op;
cin >> n;
InitHashTable(ht, n);
while(cin >> op) {
int k;
if(op == "insert") {
cin >> k;
if(!InsertHashTable(ht, k))
cout << k << " is " << "already existed in hash table\n";
} else if(op == "search") {
cin >> k;
cout << k << " is" << (SearchHashTable(ht, k) ? "" : " not") << " found\n";
} else if(op == "delete") {
cin >> k;
if(DeleteHashTable(ht, k)) {
PrintHashTable(ht);
} else
cout << k << " is " << "not existed in hash table\n";
} else if(op == "print") {
PrintHashTable(ht);
}
}
DestroyHashTable(ht);
return 0;
}//main
输入
第一行只包含一个整数n,表示哈希表的大小。
随后为哈希表的操作系列,每个操作一行,具体见样例。
输出
如果输入为"insert k",如果表中k已经存在则输出"k is already existed in hash table",否则将k插入后什么也不输出。
如果输入为"search k “,如果表中k已经存在则输出"k is found”,否则输出 “k is not found”。
如果输入为"delete k",如果表中k已经存在,则删除后则输出整个表,否则输出"k is not existed in hash table"。
如果输入为"print",则输出整个表。
样例输入
13
insert 19
insert 14
insert 14
insert 23
insert 1
print
search 68
insert 68
insert 20
insert 84
insert 27
search 68
insert 55
insert 11
insert 10
insert 79
print
insert 27
delete 1
delete 80
delete 19
delete 55
样例输出
14 is already existed in hash table
Print the hash table
[0]
[1]->14->1
[2]
[3]
[4]
[5]
[6]->19
[7]
[8]
[9]
[10]->23
[11]
[12]
68 is not found
68 is found
Print the hash table
[0]
[1]->14->1->27->79
[2]
[3]->68->55
[4]
[5]
[6]->19->84
[7]->20
[8]
[9]
[10]->23->10
[11]->11
[12]
27 is already existed in hash table
Print the hash table
[0]
[1]->14->27->79
[2]
[3]->68->55
[4]
[5]
[6]->19->84
[7]->20
[8]
[9]
[10]->23->10
[11]->11
[12]
80 is not existed in hash table
Print the hash table
[0]
[1]->14->27->79
[2]
[3]->68->55
[4]
[5]
[6]->84
[7]->20
[8]
[9]
[10]->23->10
[11]->11
[12]
Print the hash table
[0]
[1]->14->27->79
[2]
[3]->68
[4]
[5]
[6]->84
[7]->20
[8]
[9]
[10]->23->10
[11]->11
[12]
解题过程
这三段代码分别实现了哈希表的搜索、插入和删除操作。下面是对每个函数的详细解析:
函数 SearchHashTable
目的:在哈希表中搜索关键字 k
。
参数:
ht
:哈希表对象。k
:要搜索的关键字。
返回值:如果找到关键字,返回指向对应节点的指针;否则返回 NULL
。
逻辑流程:
- 计算关键字
k
的哈希索引index
。 - 从哈希表
ht
的elem
数组中获取索引index
对应的头节点temp
。 - 遍历以
temp
为头的链表:- 如果节点
temp
的关键字等于k
,则返回temp
。 - 否则,将
temp
移动到下一个节点。
- 如果节点
- 如果遍历完整条链表都没有找到关键字
k
,则返回NULL
。
函数 InsertHashTable
目的:将关键字 k
插入哈希表 ht
。
参数:
ht
:引用传递的哈希表对象。k
:要插入的关键字。
返回值:如果插入成功,返回 true
;如果关键字已存在,返回 false
。
逻辑流程:
- 使用
SearchHashTable
检查关键字k
是否已存在,如果存在,则返回false
。 - 计算关键字
k
的哈希索引index
。 - 根据索引
index
找到对应的链表头节点temp
。 - 如果链表为空(
temp == NULL
),将新节点s
放在该索引处,并返回true
。 - 如果链表不为空,遍历链表直到找到最后一个节点,将新节点
s
插入到链表尾部,并返回true
。
函数 DeleteHashTable
目的:从哈希表中删除关键字 k
的节点。
参数:
ht
:引用传递的哈希表对象。k
:要删除的关键字。
返回值:如果删除成功,返回 true
;如果关键字不存在,返回 false
。
逻辑流程:
- 使用
SearchHashTable
检查关键字k
是否存在。 - 如果存在,计算关键字
k
的哈希索引index
。 - 遍历以
ht.elem[index]
为头的链表:- 如果头节点的关键字就是
k
,则删除头节点,并返回true
。 - 如果某个节点的下一个节点的关键字是
k
,则删除该节点,并返回true
。
- 如果头节点的关键字就是
- 如果遍历完整条链表都没有找到关键字
k
,则返回false
。
注意:
PNode
应该是Node*
的别名,表示指向节点的指针。Node
表示哈希表中的节点结构,包含关键字key
和指向下一个节点的指针next
。HashTable
是哈希表结构,其中sz
表示哈希表的大小,elem
是一个数组,存储指向各个链表头节点的指针。
潜在问题:
- 如果哈希表的负载因子(即哈希表中的元素数量与哈希表大小的比例)过高,可能会导致大量冲突,从而影响性能。
- 删除操作中,如果关键字不存在,应该直接返回
false
,而不是调用SearchHashTable
。
改进建议:
- 考虑实现动态扩容机制,当哈希表的负载因子超过一定阈值时,增加哈希表的大小,并重新分配所有元素。
- 在删除操作中,直接根据哈希索引找到对应的链表,然后进行删除操作,避免不必要的搜索操作。
部分代码
//删除表中韩关键字k的结点并返回true。如果不存在,直接返回false。
bool DeleteHashTable(HashTable &ht, int k)
{
/****在此下面完成代码************/
//DeleteHashTable()函数的另一种写法。
// if (ht.elem[k%ht.sz] && ht.elem[k%ht.sz]->key==k ) {
// ht.elem[k%ht.sz] = ht.elem[k%ht.sz]->next;
// return true;
// }
// for (PNode p = ht.elem[k%ht.sz]; p&&p->next; p=p->next){
// if ( p->next->key == k ){
// p->next = p->next->next;
// return true;
// }
// }
// return false;
if(SearchHashTable(ht,k))
{
int index = k % ht.sz;
PNode temp = ht.elem[index];
while(temp)
{
if(temp->key == k)
{
ht.elem[index] = ht.elem[index]->next;
return true;
}
else if(temp->next && temp->next->key == k)
{
temp->next = temp->next->next;
return true;
}
temp = temp->next;
}
return false;
}
else
return false;
/***********************************/
}
//如果表中存在k,返回指向对应结点的指针,否则返回NULL
PNode SearchHashTable(HashTable ht, int k)
{
/****在此下面完成代码************/
PNode temp;
int index = k % ht.sz;
temp = ht.elem[index];
while(temp)
{
if(temp->key == k)
return temp;
else
temp = temp->next;
}
return NULL;
/********************************/
}
//将k插入ht中对应的链表尾部,如果表中k已经存在,直接返回false,否则插入k后返回true
bool InsertHashTable(HashTable &ht, int k)
{
/****在此下面完成代码************/
if(SearchHashTable(ht,k))
return false;
int index = k % ht.sz;
Node *temp = ht.elem[index];
Node *s;
s = new Node;
s->key = k;
s->next = NULL;
if (temp == NULL) {
ht.elem[index] = s;
return true;
}
// DEBUG
while(temp->next)
temp = temp->next;
temp->next = s;
return true;
/***********************************/
}
错误代码 and AC代码
因为bool DeleteHashTable(HashTable &ht, int k)部分的错误导致第一次没有AC,正确代码中含有错误的部分与正确部分进行对比
>AC代码
```c
#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;
#define DEBUG cout << __LINE__ << endl;
typedef struct node {
int key;
struct node *next;
} Node, *PNode;
typedef struct {
PNode *elem;
int sz; //size of hashtable
} HashTable;
void InitHashTable(HashTable &ht, int n)
{
ht.sz = n;
//申请大小为n的指针数组,每个指针初始化为NULL
ht.elem = new PNode[n]();
}
//如果表中存在k,返回指向对应结点的指针,否则返回NULL
PNode SearchHashTable(HashTable ht, int k)
{
/****在此下面完成代码************/
PNode temp;
int index = k % ht.sz;
temp = ht.elem[index];
while(temp)
{
if(temp->key == k)
return temp;
else
temp = temp->next;
}
return NULL;
/********************************/
}
//将k插入ht中对应的链表尾部,如果表中k已经存在,直接返回false,否则插入k后返回true
bool InsertHashTable(HashTable &ht, int k)
{
/****在此下面完成代码************/
if(SearchHashTable(ht,k))
return false;
int index = k % ht.sz;
Node *temp = ht.elem[index];
Node *s;
s = new Node;
s->key = k;
s->next = NULL;
if (temp == NULL) {
ht.elem[index] = s;
return true;
}
// DEBUG
while(temp->next)
temp = temp->next;
temp->next = s;
return true;
/***********************************/
}
//删除表中韩关键字k的结点并返回true。如果不存在,直接返回false。
bool DeleteHashTable(HashTable &ht, int k)
{
/****在此下面完成代码************/
//DeleteHashTable()函数的另一种写法。
// if (ht.elem[k%ht.sz] && ht.elem[k%ht.sz]->key==k ) {
// ht.elem[k%ht.sz] = ht.elem[k%ht.sz]->next;
// return true;
// }
// for (PNode p = ht.elem[k%ht.sz]; p&&p->next; p=p->next){
// if ( p->next->key == k ){
// p->next = p->next->next;
// return true;
// }
// }
// return false;
if(SearchHashTable(ht,k))
{
int index = k % ht.sz;
PNode temp = ht.elem[index];
while(temp)
{
if(temp->key == k)
{
ht.elem[index] = ht.elem[index]->next;
return true;
}
else if(temp->next && temp->next->key == k)
{
temp->next = temp->next->next;
return true;
}
temp = temp->next;
}
return false;
}
else
return false;
/***********************************/
}
static void PrintLinklist(PNode h)
{
for(PNode p = h; p; p = p->next) {
cout << "->" << p->key;
}
cout << endl;
}
void PrintHashTable(HashTable ht)
{
cout << "Print the hash table\n";
for(int i = 0; i < ht.sz; i++) {
cout << '[' << i << ']';
PrintLinklist(ht.elem[i]);
}
}
int main()
{
int n;
HashTable ht;
string op;
cin >> n;
InitHashTable(ht, n);
while(cin >> op)
{
int k;
if(op == "insert")
{
cin >> k;
if(!InsertHashTable(ht, k))
cout << k << " is " << "already existed in hash table\n";
}
else if(op == "search")
{
cin >> k;
cout << k << " is" << (SearchHashTable(ht, k) ? "" : " not") << " found\n";
}
else if(op == "delete")
{
cin >> k;
if(DeleteHashTable(ht, k))
PrintHashTable(ht);
else
cout << k << " is " << "not existed in hash table\n";
}
else if(op == "print")
PrintHashTable(ht);
}
DestroyHashTable(ht);
return 0;
}