散列表应用---电话号码查询系统
问题介绍
设计电话号码查询系统
基本要求
1.设每个记录有下列数据项:
电话号码、用户名、地址;
2.从键盘输入各记录,分别以电话号码和用户名为关键字建立散列表;
3.采用合适的方法解决冲突;
4.查找并显示给定电话号码的记录;
5.查找并显示给定用户名的记录;
6.插入用户记录;
7.删除用户记录;
8.输出用户的信息。
算法思想
1.将输入的用户的电话号码和用户名以字符串的形式存储起来。采用除留取余法将电话号码和用户名所对应的字符串的ASCII码的和求出来,对除数取余,放到散列表中。
2.对于除数怎么确定呢?首先我们先输入一个n,表示用n个用户,然后将n+10,求n到n+10里面最大的素数,作为除数。这就是除留取余法。
//判断素数 bool isprime(int n) { if (n <= 1) return false; long long m = floor(sqrt(n) + 0.5); for (long long i = 2; i <= m; ++i) { if (n%i == 0) return false; } return true; } //确定除留取余法的除数,m=n+10. int confirm(int m) { while (!isprime(m)) { //如果函数返回值是false,说明m不是素数,就把m-- --m; } return m; }
3.冲突的判定方法(以输入电话号码为例)
如果输入的电话号码的ASCII码对应的余数值对应的位置已经有元素了,我们就将这个元素位置往后移一位,并接着判断此位置是否已经有元素了。
如果判断到散列表的末尾,就接着从头开始判断。如果判断的次数等于表长,就说明此电话号码插不进去了。
while (rec1[sum].flag == true) { //如果该位置已经有数据了,就向后移动一位 if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << telnum << "插入不进散列表" << endl; break; } } if (count != m) { strcpy(rec1[sum].telnum, telnum); strcpy(rec1[sum].telname, telname); strcpy(rec1[sum].teladd, teladd); rec1[sum].flag = true; rec1[sum].count = count; }
4.查询算法和删除算法
查询算法首先需要找到对应余数值相等的位置,再判断此处的字符串是否与之匹配,如果匹配就输出,不匹配就往后移一位,这个过程和插入是类似的。
删除也是,删除只要将对应位置的标志位记为false即可。
源代码
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<string.h> #include<iomanip> using namespace std; const int maxn = 100; int n;//记录个数 int n1, n2; //n1,n2分别表示两个散列表里面的用户数 int m;//散列表大小 //用户信息 struct record { char telnum[maxn]; //电话号码 char telname[maxn]; //用户名 char teladd[maxn]; //地址 bool flag; //标志位,如果flag=false,说明此处无数据。反之,则有数据 int count = 0; //记录发生冲突的次数 }; struct record rec1[maxn]; //电话号码散列表 struct record rec2[maxn]; //用户名散列表 //判断素数 bool isprime(int n) { if (n <= 1) return false; long long m = floor(sqrt(n) + 0.5); for (long long i = 2; i <= m; ++i) { if (n%i == 0) return false; } return true; } //确定除留取余法的除数 int confirm(int m) { while (!isprime(m)) { //如果函数返回值是false,说明m不是素数,就把m-- --m; } return m; } //构建电话号码散列表 void init1() { while (n1--) { char telnum[maxn], telname[maxn], teladd[maxn]; cout << "请输入电话号码:"; cin >> telnum; cout << "请输入用户名:"; cin >> telname; cout << "请输入地址:"; cin >> teladd; int sum(0); bool flag = false; int count(0); //记录发生冲突的次数 for (int i = 0; i <strlen(telnum); ++i) { sum += telnum[i] - '0'; //计算key值 } sum = sum % m; //除留取余,找到key值在散列表中对应的位置 while (rec1[sum].flag == true) { //如果该位置已经有数据了,就向后移动一位 if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << telnum << "插入不进散列表" << endl; break; } } if (count != m) { strcpy(rec1[sum].telnum, telnum); strcpy(rec1[sum].telname, telname); strcpy(rec1[sum].teladd, teladd); rec1[sum].flag = true; rec1[sum].count = count; } } } //通过电话号码查询信息 void print1() { char t[20]; cout << "请输入需要查找的电话号码:"; cin >> t; int sum(0); for (int i = 0; i <strlen(t); ++i) { sum += t[i] - '0'; //计算key值 } sum = sum % m; //除留取余,找到key值在散列表中对应的位置 int count(0); //记录判断的次数 while (strcmp(rec1[sum].telnum, t) != 0) { if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << "查询失败" << endl; cout << t << "不在散列表中" << endl; break; } } if (count != m) { cout << "查询成功" << endl; cout << "电话号码:" << rec1[sum].telnum << endl; cout << "用户名:" << rec1[sum].telname << endl; cout << "地址:" << rec1[sum].teladd << endl; cout << "冲突次数:" << rec1[sum].count << endl; } } //根据电话号码插入用户 void insert1() { char telnum[maxn], telname[maxn], teladd[maxn]; cout << "请输入需要插入的用户的电话号码 "; cin >> telnum; int sum(0); bool flag = false; int count(0); //记录发生冲突的次数 for (int i = 0; i <strlen(telnum); ++i) { sum += telnum[i] - '0'; //计算key值 } sum = sum % m; //除留取余,找到key值在散列表中对应的位置 while (rec1[sum].flag == true) { //如果该位置已经有数据了,就向后移动一位 if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << "通讯录已满,用户电话号码为" << telnum << "的用户插入失败" << endl; break; } } if (count != m) { cout << "插入成功,请完善该用户的其他信息" << endl; cout << "请输入用户名:"; cin >> telname; cout << "请输入地址:"; cin >> teladd; strcpy(rec1[sum].telnum, telnum); strcpy(rec1[sum].telname, telname); strcpy(rec1[sum].teladd, teladd); rec1[sum].flag = true; rec1[sum].count = count; } } //根据电话号码删除用户 void Delete1() { char telnum[maxn], telname[maxn], teladd[maxn]; cout << "请输入需要删除的用户的电话号码 "; cin >> telnum; int sum(0); bool flag = false; int count(0); //记录发生冲突的次数 for (int i = 0; i <strlen(telnum); ++i) { sum += telnum[i] - '0'; //计算key值 } sum = sum % m; //除留取余,找到key值在散列表中对应的位置 while (strcmp(rec1[sum].telnum, telnum) != 0||rec1[sum].flag==false) { if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << "删除失败,电话号码为" << telnum << "的用户不在通讯录中" << endl; break; } } if (count != m) { cout << "删除成功" << endl; rec1[sum].flag = false; } } //显示电话号码散列表的信息 void fun1() { cout << "******************************" << endl; cout << "********电话号码通讯录********" << endl; cout << "******************************" << endl; for (int i = 0; i < m; ++i) { if (rec1[i].flag == true) { cout << i << "号用户的信息" << "电话号码:" << rec1[i].telnum << " " << "用户名:" << rec1[i].telname << " " << "地址:" << rec1[i].teladd << " " << "冲突次数:" << rec1[i].count << endl; } } } //计算电话号码通讯录查找成功的ASL void succ_asl1() { int counter = 0; //统计发生冲突的次数 int counter1 = 0; //统计散列表里面有多少个数据 for (int i = 0; i < m; ++i) { if (rec1[i].flag) { counter += rec1[i].count+1; ++counter1; } } double succ_asl; if (counter1 != 0) { succ_asl = (double)((double)counter / (double)counter1); } cout << counter << endl; cout<<"查找成功的ASL为 "<< setprecision(5) << fixed <<succ_asl<< endl; } //计算电话号码通讯录查找失败的ASL void fail_asl1() { int counter = 0; //统计发生冲突的次数 int counter1; //统计连续有数据的个数 for (int i = 0; i < m; ++i) { counter1 = 0; int temp = i; while(rec1[temp].flag) { if (temp == m) { temp = 0; } ++counter1; ++temp; } counter1++; counter += counter1; } double fail_asl; fail_asl = (double)((double)counter / (double)m); cout << counter << endl; cout << "查找失败的ASL为 " << setprecision(5) << fixed << fail_asl << endl; cout << counter << endl; } //构建用户名散列表 void init2() { while (n2--) { char telnum[maxn], telname[maxn], teladd[maxn]; cout << "请输入用户名(请输入英文名):"; cin >> telname; cout << "请输入电话号码:"; cin >> telnum; cout << "请输入地址:"; cin >> teladd; int sum(0); bool flag = false; int count(0); //记录发生冲突的次数 for (int i = 0; i <strlen(telname); ++i) { sum += telname[i] - 'A'; //计算key值 } sum = sum % m; //除留取余,找到key值在散列表中对应的位置 while (rec2[sum].flag == true) { //如果该位置已经有数据了,就向后移动一位 if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << telnum << "插入不进散列表" << endl; break; } } if (count != m) { strcpy(rec2[sum].telnum, telnum); strcpy(rec2[sum].telname, telname); strcpy(rec2[sum].teladd, teladd); rec2[sum].flag = true; rec2[sum].count = count; } } } //通过用户名查询信息 void print2() { char t[20]; cout << "请输入需要查找的用户名(请输入英文名):"; cin >> t; int sum(0); for (int i = 0; i <strlen(t); ++i) { sum += t[i] - 'A'; //计算key值 } sum = sum % m; //除留取余,找到key值在散列表中对应的位置 int count(0); //记录判断的次数 while (strcmp(rec2[sum].telname, t) != 0) { if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << "查询失败" << endl; cout << t << "不在用户名通讯录中" << endl; break; } } if (count != m) { cout << "查询成功" << endl; cout << "用户名:" << rec2[sum].telname << endl; cout << "电话号码:" << rec2[sum].telnum << endl; cout << "地址:" << rec2[sum].teladd << endl; cout << "冲突次数:" << rec2[sum].count << endl; } } //根据用户名插入用户 void insert2() { char telnum[maxn], telname[maxn], teladd[maxn]; cout << "请输入需要插入的用户的用户名 "; cin >> telname; int sum(0); bool flag = false; int count(0); //记录发生冲突的次数 for (int i = 0; i <strlen(telname); ++i) { sum += telname[i] - 'A'; //计算key值 } sum = sum % m; //除留取余,找到key值在散列表中对应的位置 while (rec2[sum].flag == true) { //如果该位置已经有数据了,就向后移动一位 if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << "通讯录已满,用户名为" << telname << "的用户插入失败" << endl; break; } } if (count != m) { cout << "插入成功,请完善该用户的其他信息" << endl; cout << "请输入电话号码:"; cin >> telnum; cout << "请输入地址:"; cin >> teladd; strcpy(rec2[sum].telnum, telnum); strcpy(rec2[sum].telname, telname); strcpy(rec2[sum].teladd, teladd); rec2[sum].flag = true; rec2[sum].count = count; } } //根据用户名删除用户 void Delete2() { char telnum[maxn], telname[maxn], teladd[maxn]; cout << "请输入需要删除的用户的用户名 "; cin >> telname; int sum(0); bool flag = false; int count(0); //记录发生冲突的次数 for (int i = 0; i <strlen(telname); ++i) { sum += telname[i] - '0'; //计算key值 } sum = sum % m; //除留取余,找到key值在散列表中对应的位置 while (strcmp(rec2[sum].telname, telname) != 0||rec2[sum].flag==false) { if (sum < m - 1) sum++; //如果已经移动到最后一位了,就 else sum = 0; //从头开始判断 ++count; //记录冲突次数 if (count == m) { cout << "删除失败,用户名为" << telname << "的用户不在通讯录中" << endl; break; } } if (count != m) { cout << "删除成功" << endl; rec2[sum].flag = false; } } //显示用户名散列表的信息 void fun2() { cout << "****************************" << endl; cout << "********用户名通讯录********" << endl; cout << "****************************" << endl; for (int i = 0; i < m; ++i) { if (rec2[i].flag == true) { cout << i << "号用户的信息" << "用户名:" << rec2[i].telname << " " << "电话号码:" << rec2[i].telnum << " " << "地址:" << rec2[i].teladd << " " << "冲突次数:" << rec2[i].count << endl; } } } //计算用户名通讯录查找成功的ASL void succ_asl2() { int counter = 0; //统计发生冲突的次数 int counter1 = 0; //统计散列表里面有多少个数据 for (int i = 0; i < m; ++i) { if (rec2[i].flag) { counter += rec2[i].count + 1; ++counter1; } } double succ_asl; if (counter1 != 0) { succ_asl = (double)((double)counter / (double)counter1); } cout << counter << endl; cout << "查找成功的ASL为 " << setprecision(5) << fixed << succ_asl << endl; } //计算用户名通讯录查找失败的ASL void fail_asl2() { int counter = 0; //统计发生冲突的次数 int counter1; //统计连续有数据的个数 for (int i = 0; i < m; ++i) { counter1 = 0; int temp = i; while (rec2[temp].flag) { if (temp == m) { temp = 0; } ++counter1; ++temp; } counter1++; counter += counter1; } double fail_asl; fail_asl = (double)((double)counter / (double)m); cout << counter << endl; cout << "查找失败的ASL为 " << setprecision(5) << fixed << fail_asl << endl; cout << counter << endl; } //主函数 int main() { cout << "请输入用户个数: "; cin >> n; n1 = n; n2 = n; m = confirm(n + 10); //确定散列表的大小 cout << "散列表大小为" << m << endl; while (1) { int counter(0); cout << " * * * * * * * * * * * * * * * * * * * * * * * * * * *" << endl; cout << " * * * * * * * * * 欢迎使用电话号码查询系统 * * * * * * * * * * *" << endl; cout << " * *" << endl; cout << " * 1.根据电话号码构建散列表 2.根据用户名构建散列表 *" << endl; cout << " * 3.根据电话号码查询相关信息 4.根据用户名查询相关信息 *" << endl; cout << " * 5.根据电话号码插入用户信息 6.根据用户名插入用户信息 *" << endl; cout << " * 7.根据电话号码删除散列表信息 8.根据用户名删除散列表信息 *" << endl; cout << " * 9.显示电话号码散列表信息 10.显示用户名散列表信息 *" << endl; cout << " * 11.求电话号码散列表查找成功的ASL 12.求用户名散列表查找成功的ASL *" << endl; cout << " * 13.求电话号码散列表查找失败的ASL 14.求用户名散列表查找失败的ASL *" << endl; cout << " * 15.清屏 16.安全退出 *" << endl; cout << " * *" << endl; cout << " * * * * * * * * * * * * * * * * * * * * * * * * * * *" << endl; cout << "请输入数字进行操作 "; cin >> counter; switch (counter) { case 1: { init1(); cout << endl << endl; }; break; case 2: { init2(); cout << endl << endl; }; break; case 3: { print1(); cout << endl << endl; }; break; case 4: { print2(); cout << endl << endl; }; break; case 5: { insert1(); cout << endl << endl; }; break; case 6: { insert2(); cout << endl << endl; }; break; case 7: { Delete1(); cout << endl << endl; }; break; case 8: { Delete2(); cout << endl << endl; }; break; case 9: { fun1(); cout << endl << endl; }; break; case 10: { fun2(); cout << endl << endl; }; break; case 11: { succ_asl1(); cout << endl << endl; }; break; case 12: { succ_asl2(); cout << endl << endl; }; break; case 13: { fail_asl1(); cout << endl << endl; }; break; case 14: { fail_asl2(); cout << endl << endl; }; break; case 15: { system("cls"); }; break; case 16: { return 0; }; break; } } }
效果展示
ENDing
呃呃呃,没有ending,鄙人贼菜,有什么好的意见,评论区留言哈! :)
谁人畏惧现实,谁人将从梦中消失
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构