通讯录查询系统的设计与实现

【实验目的】

  1. 掌握哈希表的创建方法
  2. 学会使用二次探测再散列法解决冲突
  3. 练习文件写入操作

【实验原理】

  1. 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
  2. 二次探测再散列法: 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 11, − 1 ∗ 1 -1*1 11, 2 ∗ 2 2*2 22, − 2 ∗ 2 -2*2 22, ……, K ∗ K K*K KK, − K ∗ K -K*K KK ( K ≤ m / 2 ) (K≤m/2) (Km/2)
  3. 通过fopen()、fputs()、fputc()进行文件写入操作。

【实验内容】

  1. 问题描述:
    为某个单位建立一个员工通讯录管理系统,可以方便查询每一个员工的电话与地址。设计散列表存储,设计并实现通讯录查找系统。
  2. 基本要求 :
    (1)每个记录有下列数据项:电话号码、用户名、地址;
    (2)从键盘输入各记录,分别以电话号码为关键字建立散列表;
    (3)采用二次探测再散列法解决冲突;
    (4)查找并显示给定电话号码的记录;
    (5)通讯录信息文件保存。
  3. 重点、难点
    重点:
    (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”文件
在这里插入图片描述

  • 10
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值