【实验目的】
- 掌握哈希表的创建方法
- 学会使用二次探测再散列法解决冲突
- 练习文件写入操作
【实验原理】
- 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
- 二次探测再散列法: D = H ( k e y ) D = H(key) D=H(key); N D = ( D + d i ) ND = (D+ di) ND=(D+di)% m m m; d i di di取 1 ∗ 1 1*1 1∗1, − 1 ∗ 1 -1*1 −1∗1, 2 ∗ 2 2*2 2∗2, − 2 ∗ 2 -2*2 −2∗2, ……, K ∗ K K*K K∗K, − K ∗ K -K*K −K∗K ( K ≤ m / 2 ) (K≤m/2) (K≤m/2)
- 通过fopen()、fputs()、fputc()进行文件写入操作。
【实验内容】
- 问题描述:
为某个单位建立一个员工通讯录管理系统,可以方便查询每一个员工的电话与地址。设计散列表存储,设计并实现通讯录查找系统。 - 基本要求 :
(1)每个记录有下列数据项:电话号码、用户名、地址;
(2)从键盘输入各记录,分别以电话号码为关键字建立散列表;
(3)采用二次探测再散列法解决冲突;
(4)查找并显示给定电话号码的记录;
(5)通讯录信息文件保存。 - 重点、难点
重点:
(1)通过实验深入理解哈希表既是一种存储形式,又是一种查找方法;
(2)哈希表的构造;
(3)哈希冲突方案的设计。
难点:哈希表的构造与哈希冲突方案的设计
【程序代码与运行结果】
main.cpp
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
#define MAXSIZE 10 //通讯录记录数量
#define HASHSIZE 20 //定义表长
using namespace std;
int NUM_NOW = 0; //记录当前表长
typedef struct //每一条记录的信息;
{
char Number[30]; //号码
char Name[20]; //姓名
char Address[50]; //地址
}Records;
typedef struct Hash
{ //哈希表
Records *record; //数据元素存储基址
int records_num; //当前数据元素个数
int max_size; //最大容量
}*HashTable, HashElem;
//哈希函数,将电话号码每一位求和
int GetHashKey(char ar[])
{
int len = strlen(ar);
int key = 0;
for (int i = 0; i < len; i++){
key += ar[i] - '0'; //取每个字符对应ASCII码减去‘0’对应ASCII码
} //得到每个数字整数值
return key % HASHSIZE; //取模,否则下标越界
}
//二次探测再散列法处理冲突
int HandleCollision(HashTable table, int key)
{
int i = 1; //从2,3,4,5,.......
while (1)
{
i++; //从2,3,4,5,.......
if (i % 2 == 0) { //key=key+(i/2)*(i/2)
if (table->record[(key + (i / 2)*(i / 2)) % MAXSIZE].Name[0] == 0) { //若当前位置未被占用
return (key + (i / 2)*(i / 2)) % MAXSIZE;
}
else if (i % 2 != 0) { //key=key-(i/2)*(i/2)
if ((key - (i / 2)*(i / 2)) < 0)
continue; //由于是减法,要注意负数不能取模
}
if (table->record[(key - (i / 2)*(i / 2)) % MAXSIZE].Name[0] == 0) { //若当前位置未被占用
return (key - (i / 2)*(i / 2)) % MAXSIZE;
}
}
}
return -1;
}
//创建哈希表
void CreateHashTable(HashTable &table, Records *record, int n)
{
int key;
for (int i = 0; i < n; i++)
{
key = GetHashKey(record[i].Number);
if (table->record[key].Name[0] != 0) { //当前位置被占用,即发生冲突
key = HandleCollision(table, key); //调用二次探测再散列法解决冲突
}
strcpy_s(table->record[key].Number, record[i].Number);
strcpy_s(table->record[key].Name, record[i].Name); //输入当前位置信息
strcpy_s(table->record[key].Address, record[i].Address);
}
table->records_num += n;
cout << endl << "成功建立哈希表!" << endl;
cout << "散列表容量为" << HASHSIZE << ", 当前占用容量为" << table->records_num << endl;
}
//键盘输入联系人的记录
void InputRecord(Records *a, HashTable &H)
{
cout << endl;
cout << "请输入联系人数量:";
cin >> NUM_NOW;
int i;
cout << "请依次输入联系人的姓名、电话号码、地址,用Tab键隔开" << endl;
cout << "示例:方鸿渐\t0564-9111110#\t克莱登大学围城校区" << endl << endl;
cout << "●\t姓名\t电话号码\t地址" << endl;
for (i = 0; i < NUM_NOW; i++)
{
cout << i + 1 << "\t";
cin >> a[i].Name >> a[i].Number >> a[i].Address;
}
CreateHashTable(H, a, NUM_NOW); //建立散列表存储
cout << endl;
}
//按照电话号码寻找
void Search(HashTable table)
{
cout << endl;
fflush(stdin);
char tel[11];
int p, i;
cout << "输入联系人号码: ";
cin >> tel;
int key = GetHashKey(tel);
if (strcmp(table->record[key].Number, tel)) //若当前位置对应号码与要查找号码不一致
//则说明发生过冲突
key = HandleCollision(table, key); //调用函数找到对应位置
if (strcmp(tel, table->record[key].Number) == 0)
{
cout << endl << "查找成功" << endl;
cout << " 姓名:" << table->record[key].Name << endl;
cout << " 电话:" << table->record[key].Number << endl;
cout << " 地址:" << table->record[key].Address << endl;
}
else {
cout << endl << "查无此人!" << endl;
}
cout << endl;
}
//保存至文件
void SaveToFile(Records *a, char file[])
{
char ch;
FILE *fp;
int i;
cout << endl;
cout << "为通讯录创建一个文件,请输入文件名:" << endl;
cin >> file;
if ((fp=fopen(file, "w")) == NULL)
{
cout << "保存失败!" << endl;
exit(0);
}
ch = getchar();
for (i = 0; i < NUM_NOW; i++)
{
// 每行保存一个联系人信息
fputs(a[i].Name, fp);
fputc('\t', fp);
fputs(a[i].Number, fp);
fputc('\t', fp);
fputs(a[i].Address, fp);
fputc('\n', fp);
}
fclose(fp);
cout << "保存成功!" << endl;
cout << endl;
}
//显示菜单
void Menu()
{
cout << "┏〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓┓" << endl;
cout << "┃ 按1键,创建新的通讯录 ┃" << endl;
cout << "┃ 按2键,查询某联系人信息 ┃" << endl;
cout << "┃ 按3键,将通讯录保存至文件 ┃" << endl;
cout << "┃ 按4键,退出程序 ┃" << endl;
cout << "┗〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓┛" << endl;
}
//主函数
int main()
{
Records record[MAXSIZE];
HashElem table;
HashTable numbertable;
numbertable = &table;
numbertable->record = (Records*)malloc(sizeof(record[0])*HASHSIZE);
memset(numbertable->record, 0, sizeof(record[0])*HASHSIZE);
numbertable->max_size = HASHSIZE;
numbertable->records_num = 0;
FILE *fp;
char filename[10];
int choice = 0;
while (choice != 4) {
Menu();
cout << ">>";
cin >> choice;
switch (choice)
{
case 1:
InputRecord(record, numbertable);
break; //创建新的通讯录。
case 2:
Search(numbertable);
break; //查询某联系人的信息
case 3:
SaveToFile(record, filename);
break; //保存至文件
case 4:
return 0;//退出程序。
default:
break;
}
system("pause");
system("cls");
}
return 0;
}
运行结果
输入联系人信息
张鸿渐 0564-10001 中国-安徽-合肥
王老八 0564-12123 英国-苏格兰-爱丁堡
郭添乱 0564-12306 加拿大-魁北克-蒙特利尔
吴富婆 0564-12580 澳大利亚-维多利亚-墨尔本
负心杰 0564-12345 美国-新墨西哥-阿尔伯克基
查询联系人信息
将所有联系人保存到文件中
生成的“Avengers.txt”文件