《程序设计综合设计》课程设计--电话号码查询系统

这篇具有很好参考价值的文章主要介绍了《程序设计综合设计》课程设计--电话号码查询系统。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

2.问题描述

1、设每个记录有下列数据项:电话号码、用户名、地址;

2、从键盘输入各记录,分别以电话号码和用户名为关键字建立哈希表;

3、查找并显示给定电话号码的记录;

4、查找并显示给定用户名的记录。

5、在哈希函数确定的前提下,使用各种不同类型处理冲突的方法(开放定址法、  拉链法),讨论平均查找长度的变化。

3.需求分析

3.1 数据需求

    电话号码查询系统需要存储的数据项有电话号码、用户名、地址三个字段。

3.2 基本功能需求

1.需要使用两个解决冲突的方式来进行hash存储

2.分别使用姓名和电话号两个属性进行hash存储。

3.可以插入新的用户记录,包括电话号码、用户名、地址三个字段。

4.可以查询用户记录: 可以通过电话号码或姓名来查询用户记录。

5.开放定址法: 使用数组存储数据,当发生冲突时,通过查找下一个空闲单元来解决冲突。

6.链地址法: 使用链表存储数据,当发生冲突时,将新数据插入链表中,通过链表来解决冲突

3.3 非功能性需求

用户界面需求:简洁、易用、易懂、友好的用户界面。

硬件要求:装有Visual Studio 2019的计算机。

可靠性需求:保证用户在正常使用本系统时,用户的操作或误操作不会产生数据的丢失。

4.概要设计

4.1 数据结构

  • 开放定址法: 使用 HashNode 类型的数组来存储数据,每个元素包含一个Record_1类型的data字段和一个int类型的is_empty字段。

  • 链地址法: 使用结构体Record_2存储数据,每个结构体包含电话号码、用户名、地址三个字段和一个指向下一个结构体的指针next。

4.2 系统包含的函数

  1. hash_func_1: 使用简单的哈希函数计算给定字符串的哈希值
  2. search_by_phone_1: 通过电话号码在哈希表中搜索记录,并打印该记录的姓名和地址
  3. search_by_name_1: 通过姓名在哈希表中搜索记录,并打印该记录的电话号码和地址
  4. insert_data_by_phone_1: 在哈希表中插入一条新记录,根据电话号码计算哈希值
  5. print_avg_lengths_phone_1: 打印哈希表中每个桶的平均长度,根据电话号码计算哈希值
  6. print_avg_lengths_name_1: 打印哈希表中每个桶的平均长度,根据姓名计算哈希值
  7. insert_data_by_name_1: 在哈希表中插入一条新记录,根据姓名计算哈希值
  8. init_hash_table_2: 初始化哈希表,将所有元素的next指针设为NULL
  9. hash_by_phone_2: 计算电话号码的哈希值
  10. hash_by_name_2: 计算姓名的哈希值
  11. display_record_2: 打印一条记录的电话号码,姓名和地址
  12. insert_by_phone_2: 插入一条新记录,根据电话号码计算哈希值
  13. insert_by_name_2: 插入一条新记录,根据姓名计算哈希值
  14. search_by_phone_2: 通过电话号码在哈希表中搜索记录,并打印该记录的电话号码,姓名和地址
  15. search_by_name_2: 通过姓名在哈希表中搜索记录,并打印该记录的电话号码,姓名和地址
  16. print_avg_lengths_by_name_2: 打印哈希表中每个桶的平均长度,根据姓名计算哈希值
  17. print_avg_lengths_by_phone_2: 打印哈希表中每个桶的平均长度,根据电话号码计算哈希值
  18. main2(): 主函数,用于测试哈希表2
  19. main1(): 主函数,用于测试哈希表1

4.3 函数间的关系

  1. hash_func_1, hash_by_phone_2, hash_by_name_2 这三个函数都是用于计算哈希值的,hash_func_1是用来计算第一个哈希表中哈希值的,hash_by_phone_2和hash_by_name_2是用来计算第二个哈希表中哈希值的。
  2. insert_by_phone_2和insert_by_name_2这两个函数是用来在第二个哈希表中插入记录的,它们使用了hash_by_phone_2和hash_by_name_2函数来计算哈希值
  3. search_by_phone_1,search_by_name_1,search_by_phone_2, search_by_name_2这四个函数都是用来在哈希表中搜索记录的,它们分别对应第一个哈希表和第二个哈希表中搜索电话号码和姓名。
  4. display_record_2函数是用来打印第二个哈希表中搜索到的记录的电话号码,姓名和地址。
  5. init_hash_table_2函数是用来初始化第二个哈希表的,它会将所有的链表指针都设置为NULL。
  6. main2()和main1()是程序的主入口,它们分别调用了第一个哈希表和第二个哈希表的插入,查询和显示函数来测试哈希表的功能。

4.4 系统功能模块图

《程序设计综合设计》课程设计--电话号码查询系统

5.详细设计

5.1 结构体的详细定义

typedef struct {
    char phone[20];//电话号码
    char name[20];//姓名
    char address[100];//地址
} Record_1;

typedef struct {//结构体定义
    Record_1 data;
    int is_empty;
} HashNode;

struct Record_2 {
    char phone[20];
    char name[20];
    char address[100];
    struct Record_2* next;
}

5.2 系统函数详细介绍

  1. hash_func_1: 使用简单的哈希函数计算给定字符串的哈希值
  2. search_by_phone_1: 通过电话号码在哈希表中搜索记录,并打印该记录的姓名和地址
  3. search_by_name_1: 通过姓名在哈希表中搜索记录,并打印该记录的电话号码和地址
  4. insert_data_by_phone_1: 在哈希表中插入一条新记录,根据电话号码计算哈希值
  5. print_avg_lengths_phone_1: 打印哈希表中每个桶的平均长度,根据电话号码计算哈希值
  6. print_avg_lengths_name_1: 打印哈希表中每个桶的平均长度,根据姓名计算哈希值
  7. insert_data_by_name_1: 在哈希表中插入一条新记录,根据姓名计算哈希值
  8. init_hash_table_2: 初始化哈希表,将所有元素的next指针设为NULL
  9. hash_by_phone_2: 计算电话号码的哈希值
  10. hash_by_name_2: 计算姓名的哈希值
  11. display_record_2: 打印一条记录的电话号码,姓名和地址
  12. insert_by_phone_2: 插入一条新记录,根据电话号码计算哈希值
  13. insert_by_name_2: 插入一条新记录,根据姓名计算哈希值
  14. search_by_phone_2: 通过电话号码在哈希表中搜索记录,并打印该记录的电话号码,姓名和地址
  15. search_by_name_2: 通过姓名在哈希表中搜索记录,并打印该记录的电话号码,姓名和地址
  16. print_avg_lengths_by_name_2: 打印哈希表中每个桶的平均长度,根据姓名计算哈希值
  17. print_avg_lengths_by_phone_2: 打印哈希表中每个桶的平均长度,根据电话号码计算哈希值
  18. main2(): 主函数,用于测试哈希表2
  19. main1(): 主函数,用于测试哈希表1

5.3 系统功能模块介绍

本系统包括两个模块:

一个是线性探测模块(如图5-3-1),一个是拉链法模块(如图5-3-2)。

《程序设计综合设计》课程设计--电话号码查询系统

 图5-3-1线性探测法模块流程图

《程序设计综合设计》课程设计--电话号码查询系统

 

图5-3-2 拉链法模块流程图

5.4 各相关数据结构、算法间的对比

  1. search_by_phone_1: 根据电话号码在第一个哈希表中查找记录。首先使用hash_func_1函数计算电话号码的哈希值,然后在哈希表中循环查找,直到找到对应的记录或者遍历完整个哈希表。如果找到,则输出记录的姓名和地址,如果没有找到,则输出没有找到该人员信息。
  2. search_by_name_1: 根据姓名在第一个哈希表中查找记录。首先使用hash_func_1函数计算姓名的哈希值,然后在哈希表中循环查找,直到找到对应的记录或者遍历完整个哈希表。如果找到,则输出记录的电话号码和地址,如果没有找到,则输出没有找到该人员信息。
  3. insert_data_by_phone_1: 根据电话号码在第一个哈希表中插入记录。首先使用hash_func_1函数计算电话号码的哈希值,然后在哈希表中循环查找空闲位置插入记录。如果遍历了整个哈希表都没有空闲位置,则输出错误信息。
  4. insert_data_by_name_1: 根据姓名在第一个哈希表中插入记录。首先使用hash_func_1函数计算姓名的哈希值,然后在哈希表中循环查找空闲位置插入记录。如果遍历了整个哈希表都没有空闲位置,则输出错误信息。
  5. print_avg_lengths_phone_1:计算第一个哈希表中电话号码的平均查找长度。这个函数会遍历整个哈希表,查找每个记录对应的电话号码的查找长度,然后计算总长度的平均值。
  6. print_avg_lengths_name_1: 与print_avg_lengths_phone_1类似,计算第一个哈希表中姓名的平均查找长度。
  7. insert_by_phone_2: 根据电话号码在第二个哈希表中插入记录。首先hash_by_phone_2函数计算电话号码的哈希值,然后在对应的哈希值位置插入新节点。
  8. search_by_phone_2根据电话号码在第二个哈希表中查找记录:首先使用hash_by_phone_2函数计算电话号码的哈希值。根据哈希值找到相应的链表在链表中遍历每一个节点,如果找到匹配的电话号码输出信息,否则输出没有找到。
  9. insert_by_name_2: 根据姓名在第二个哈希表中插入记录。首先hash_by_name_2函数计算姓名的哈希值,然后在对应的哈希值位置插入新节点。
  10. search_by_name_2根据姓名在第二个哈希表中查找记录:首先使用hash_by_name_2函数计算姓名的哈希值。根据哈希值找到相应的链表在链表中遍历每一个节点,如果找到匹配的姓名输出信息,否则输出没有找到。

数据结构对比:

  1. 线性探测法(Open Addressing):当发生冲突时,在哈希表中往后探测一个单位(如+1或+2),直到找到一个空位置。优点是在哈希表中使用空间更紧凑,缺点是当哈希表较满时,查询性能会受到影响。

  2. 拉链法(Chaining):当发生冲突时,将哈希值相同的记录放入一个链表中。优点是当哈希表较满时,查询性能依然较好,缺点是需要额外的空间来存储

改进思路:开放定址法中可以使用双重散列函数,以减少冲突的可能。界面应该设计更加人性化,方便非专业人员使用,以及提高代码的健壮性,对程序进行不断的测试,测试出程序中,不严谨问题。

6.调试分析

本软件是基于Windows的编程开发,所以,软件调试必须在Windows环境下进行。调试前须做好准备工作:

1.需要安装Visual Studio 2019的计算机一台;

配置好之后,在Visual Studio 2019环境下进行软件的调试

2.测试数据:

冲突选择输入:

main”模块根据控制台主界面提示选择解决冲突的方式,键入数字1,会选择线性探测法(开放定址法),键入2会选择拉链法。 

在“查询数据”模块中,根据主界面提示,键入数字2,确定,再按提示进行操作测试,键入姓名如小黑,确定,程序显示该姓名相关记录的所有信息,如果没有找到该姓名的记录,程序提示“该人员没有找到”

3.算法改进设想:

开放定址法中可以使用双重散列函数,以减少冲突的可能。界面应该设计更加人性化,方便非专业人员使用,以及提高代码的健壮性,对程序进行不断的测试,测试出程序中,不严谨问题。

4.使用说明

1.电话号码为关键字建立的哈希表,按电话号码查询,线性探测法解决冲突,成功找到该人员并打印该人员信息和平均查找成功和失败长度。(如图7-1)

《程序设计综合设计》课程设计--电话号码查询系统

 图7-1

2.电话号码为关键字建立的哈希表,按电话号码查询,拉链法解决冲突,成功找到该人员并打印该人员信息和平均查找成功和失败长度。(如图7-2)

《程序设计综合设计》课程设计--电话号码查询系统 如图7-2

3.姓名为关键字建立的哈希表,按姓名查询,线性探测法解决冲突,成功找到该人员并打印该人员信息和平均查找成功和失败长度。(如图7-3)

如图7-3

4.姓名为关键字建立的哈希表,按姓名查询,拉链法解决冲突,成功找到该人员并打印该人员信息和平均查找成功和失败长度。(如图7-4)

 如图7-4

5.电话号码为关键字建立的哈希表,按电话号码查询,线性探测法解决冲突,没有找到该人员,打印平均查找成功和失败长度。(如图7-5)

(如图7-5)

6.电话号码为关键字建立的哈希表,按电话号码查询,拉链法解决冲突,没有找到该人员,打印平均查找成功和失败长度(如图7-6)

《程序设计综合设计》课程设计--电话号码查询系统

 

(如图7-6)

7.源代码(首尾一小部分,中间部分略)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define SIZE  7

typedef struct {
    char phone[20];//电话号码
    char name[20];//姓名
    char address[100];//地址
} Record_1;

typedef struct {//结构体定义
    Record_1 data;
    int is_empty;
} HashNode;

struct Record_2 {
    char phone[20];
    char name[20];
    char address[100];
    struct Record_2* next;
};

unsigned int hash_func_1(const char* str) {//使用简单的哈希函数计算给定字符串的哈希值

    unsigned int hash = 0;

    while (*str) {

        hash = hash * 31 + *str++;

    }

    return hash % SIZE;

}

void insert_data_by_phone_1(HashNode table[], Record_1 record_1) {//在哈希表中插入一条新记录,根据电话号码计算哈希值(第一个哈希表)

    unsigned int index = hash_func_1(record_1.phone);

    int cnt = 1;

    while (1) {

        if (table[index].is_empty == 0) {

            table[index].data = record_1;

            table[index].is_empty = 1;

            return;

        }

        if (strcmp(table[index].data.phone, record_1.phone) == 0) {

            printf("该电话 %s 已经存在\n", record_1.phone);

            return;

        }

        index = (index + 1) % SIZE;

        cnt++;

        if (cnt == SIZE) {

            printf("hash表已满!\n");

            return;

        }

    }

}

int search_by_phone_1(HashNode table[], const char* phone) {//通过电话号码在哈希表中搜索记录,并打印该记录的姓名和地址(第一个哈希表)

    unsigned int index = hash_func_1(phone);

    int cnt = 0;

    while (1) {

        if (table[index].is_empty == 0) {

            printf("没有找到该人员信息!\n");

            return cnt;

        }

        if (strcmp(table[index].data.phone, phone) == 0) {

            printf("姓名: %s, 地址: %s\n", table[index].data.name, table[index].data.address);

            return cnt;

        }

        index = (index + 1) % SIZE;

        cnt++;

        if (cnt == SIZE) {

            printf("没有找到该人员信息!\n");

            return cnt;

        }

    }

}

int search_by_phone_11(HashNode table[], const char* phone) {
    unsigned int index = hash_func_1(phone);
    int cnt = 0;
    while (1) {
        if (table[index].is_empty == 0) {
            //printf("没有找到该人员信息!\n");
            return cnt;
        }
        if (strcmp(table[index].data.phone, phone) == 0) {
            //printf("姓名: %s, 地址: %s\n", table[index].data.name, table[index].data.address);
            return cnt;
        }
        index = (index + 1) % SIZE;
        cnt++;
        if (cnt == SIZE) {
            //printf("没有找到该人员信息!\n");
            return cnt;
        }
    }
}


void print_avg_lengths_phone_1(HashNode table[]) {//打印哈希表中每个桶的平均长度,根据电话号码计算哈希值(第一个哈希表)
    char phone[20] = "qwer";
    int total_count = 0;
    int total_length = 0;
    int fail_count = 0;
    int fail_length = 0;
    for (int i = 0; i < SIZE; i++) {
        if (table[i].is_empty == 1) {
            total_count++;
            total_length += search_by_phone_11(table, table[i].data.phone);
            fail_length += search_by_phone_11(table, phone);
        }

    }

    printf("平均查找成功长度: %f\n", (float)total_length / total_count);//计算第一个哈希表中电话号码的平均查找长度(第一个哈希表)
    printf("平均查找失败长度: %f\n", (float)fail_length / SIZE);
}

(中间部分略)。。。。。。

int main() {

    int choice;

    while (1) {

        printf("\n-------------------------------------------------------------\n");

        printf("*****************欢迎使用电话号码查询系统********************\n");

        printf("-------------------------------------------------------------\n");

        printf("请选择解决冲突的方法:\n");

        printf("\t\t\t\t\t\t1.开放定址法\n");

        printf("\t\t\t\t\t\t2.拉链法\n");

        printf("\t\t\t\t\t\t请选择!\n");

        printf("请输入:");

        scanf("%d", &choice);

        if (choice == 1) {

            main1();

        }

        else if (choice = 2) {

            main2();

        }

        else {

            printf("输入错误!");

        }

    }

}

8.设计总结

在进行本次实验时,我学会了使用哈希表的相关算法,包括线性探测法和拉链法,并对比了这两种算法在实现上的不同。我还学会了在 C 语言中使用结构体来存储数据,以及如何使用链表来实现拉链法。

在实验过程中,我首先使用了线性探测法实现哈希表,并在该算法的基础上改进了拉链法的实现。通过对比两种算法的实现,我发现拉链法更加灵活,能够解决线性探测法中的冲突问题。

在实验中我还学会了如何对哈希表中的数据进行插入、查找等操作。通过实验,我们深刻地理解了哈希表在实际应用中的重要性,并增强了对算法实现的理解。

在实验过程中遇到了许多问题,包括:

  1. 计算姓名和电话号码的哈希值:使用hash_by_phone_2函数计算电话号码的哈希值,使用hash_by_name_2函数计算姓名的哈希值。

int hash_by_phone_2(char* phone) {//计算电话号码的哈希值

    int hash = 0;

    for (int i = 0; i < strlen(phone); i++) {

        hash = (hash * 10 + phone[i] - '0') % SIZE;

    }

    return hash;

}

int hash_by_name_2(char* name) {//计算姓名的哈希值

    int hash = 0;

    for (int i = 0; i < strlen(name); i++) {

        hash = (hash * 26 + name[i] - 'A') % SIZE;

    }

    return hash;

}

  1. 平均查找成功和失败长度的计算:如用print_avg_lengths_phone_1函数计算第一个哈希表中电话号码的平均查找长度。这个函数会遍历整个哈希表,查找每个记录对应的电话号码的查找长度,然后计算总长度的平均值,其中平均查找成功长度=对应的电话号码的查找成功总长度/哈希表元素个数;平均查找失败长度=对应的电话号码的查找失败总长度/哈希表表长。
  2. 调试过程中,出现返回值scanf被忽略,取消NULL指针对new_node的引用等警告,通过关闭配置属性里的SDL检查解决等等。
  3. 不足之处:没有实现用txt文件来读出与写入,保存学生信息还有不熟练怎么向他人讲述清楚自己的代码等等,这些还需要自己下去更多的查阅资料和练习。
  4. 比较好的部分是,实现了不同冲突解决办法的平均长度比较。

总之,本次实验让我深入理解了哈希表的算法原理,并且提高了我的编程能力,为以后的算法学习打下了坚实的基础。

9.参考文献

[1]Robert L. Kruse,Data Structures And Program Design in C++,高等教育出版社,2001.5.

[2]严蔚敏等编著,数据结构(C 语言版),清华大学出版社,1997.4;

[3]赵文静等编著,数据结构与算法,科学出版社,2005.08;

[4]Clifford,A.Shaffer编著,数决结构与算法分析(C++版),电子工业出版社,2005.7

[5] 赵文静编著,数据结构-C++语言描述,西安交通大学出版社,1999.01

[6] 孙鑫,于安萍编著,VC++深入详解,电子工业出版社,2007.7

[7] COHOON & DAVIDSON编著,C++程序设计,清华大学出版社,2005.8文章来源地址https://www.toymoban.com/news/detail-493088.html

到了这里,关于《程序设计综合设计》课程设计--电话号码查询系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 17. 电话号码的字母组合

    给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 示例 2: 示例 3: 提示: 0 = digits.length = 4 digits[i] 是范围 [\\\'2\\\', \\\'9\\\'] 的一个数字。 解答

    2024年02月15日
    浏览(42)
  • 电话号码的字母组合-算法

    按电话上数字与字母的对应关系,如2={a,b,c},3={d,e,f}等,给定一串数字如267,则求出abc,mno,qprs的所有组合,如amq,amp...cor,cos等 遍历都可以用回溯的方式尝试解决,每次遍历结束后,将上一层元素删除,满足长度,则加入到结果中      

    2024年01月22日
    浏览(40)
  • android 实现拨打电话号码。

    在拨打电话号码之前,预设一个B号码,正常使用电话时,本来输入的是A号码。实际拨打的是B号码。但是接听页面显示的是A号码。是不是比较绕,在android9之前,各厂商的实现不了,android7以下可以实现。但是现在很多机型最低都是11以上了。 兴趣使然,研究了几天,终于出

    2024年02月14日
    浏览(45)
  • 正则表达式 - 电话号码

            正则表达式是描述一组字符串特征的模式,用来匹配特定的字符串。         写一个正则表达式匹配电话号码,并且括号、连字符或点号都是可选的。假定合规数据只包含以下15种匹配模式之一: xxxxxxx             8277019 xxx.xxxx            827.7019 xxx-xxxx      

    2023年04月23日
    浏览(46)
  • 判断电话号码是否重复-excel

    有时候重复的数据不需要或者很烦人,就需要采取措施,希望以下的方法能帮到你。 方法一: 1)针对第一个单元格输入等号,以及公式countif(查找记录数的范围,需要查找的单元格) 2)输入enter之后,然后鼠标变为黑色的加号后,往下拉自动填充,如下图所示。 方法二:先

    2024年02月03日
    浏览(50)
  • 17. 电话号码的字母组合(回溯)

    从第一个数字开始遍历其对应的字母,将其加入StringBuffer中,继续深度优先搜索,当访问到最后一个数字的时候,将StringBuffer存储到ans中,然后回溯到下一个对应字母。 拓展: StringBuffer中的删除对应字符的方法是 deleteCharAt()

    2024年01月15日
    浏览(43)
  • leetcode 17 电话号码字母组合

    给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 输入:digits = “23” 输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”] 示

    2024年01月18日
    浏览(38)
  • Leetcode17电话号码的组合

    思路:用字典的形式保存号码的映射,实际组合是前一个数字串的组合加上后面一个数字的所有可能组合

    2024年02月10日
    浏览(43)
  • Leetcode 17 电话号码的字母组合

    理解题意 :         给定一个仅包含数字  2-9  的字符串,返回所有它能表示的字母组合                 本质上:数字代表着一个字母集合                 数字的个数决定了递归的深度,即树的深度                 数字代表的字母组合决定了当前树的宽度

    2024年02月05日
    浏览(40)
  • leetcode 17. 电话号码的字母组合

             该题也是经典回溯题。 与之前做的组合有两点不同: 之前的组合题是求同一集合的组合,而本题是求不同集合的组合。 本题还需要有一个将字符串数字转换为手机号9键对应字符集的过程。         下面上代码:         如果面试中的话要注意判断异常输入的情

    2024年02月16日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包