基于顺序表的通讯录系统设计与实现

前言

在实际编程学习中,将数据结构理论应用于具体项目是提升编程能力的重要途径。本文将通过一个完整的通讯录系统实现,展示如何利用顺序表这一基础数据结构来解决实际问题。通讯录作为日常生活中常用的工具,其背后蕴含的数据管理思想值得我们深入探讨。

目录

前言

系统架构设计

整体架构概述

核心数据结构分析

联系人信息结构

顺序表与通讯录的关联

核心功能模块详解

[1. 系统初始化与销毁](#1. 系统初始化与销毁)

[2. 联系人添加功能](#2. 联系人添加功能)

[3. 联系人查询与展示](#3. 联系人查询与展示)

[4. 联系人删除功能](#4. 联系人删除功能)

[5. 联系人修改功能](#5. 联系人修改功能)

[6. 用户界面设计](#6. 用户界面设计)

测试代码分析

自动化测试函数

交互式主程序

顺序表底层实现分析

动态扩容机制

指定位置操作

系统优势与局限性

优势

局限性

扩展建议

性能优化

功能增强

用户体验改进

总结


系统架构设计

整体架构概述

本通讯录系统采用典型的三层架构:

  • 数据层:基于顺序表实现的数据存储

  • 业务层:通讯录的核心功能模块

  • 表示层:用户交互界面

这种分层设计使得系统具有良好的可维护性和扩展性。

核心数据结构分析

联系人信息结构

复制代码
// Contact.h
#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100

typedef struct personInfo
{
    char name[NAME_MAX];      // 姓名
    char gender[GENDER_MAX];  // 性别
    int age;                  // 年龄
    char tel[TEL_MAX];        // 电话
    char addr[ADDR_MAX];      // 地址
}peoInfo;

设计特点

  • 使用固定长度字符数组,简化内存管理

  • 字段设计覆盖基本联系信息

  • 宏定义统一管理字段长度,便于维护

顺序表与通讯录的关联

复制代码
// SeqList.h
typedef peoInfo SLDataType;  // 关键的类型重定义

typedef struct SeqList
{
    SLDataType* arr;    // 指向联系人数组
    int size;           // 当前联系人数量
    int capacity;       // 容量
}SL;

// Contact.h
typedef struct SeqList Contact;  // 类型别名,增强可读性

设计优势

  • 通过类型重定义实现数据结构复用

  • 通讯录直接继承顺序表的所有特性

  • 代码具有良好的抽象层次

核心功能模块详解

1. 系统初始化与销毁

初始化函数

复制代码
void ContactInit(Contact* con)
{
    SLInit(con);  // 复用顺序表初始化
}

销毁函数

复制代码
void ContactDesTroy(Contact* con)
{
    SLDestroy(con);  // 复用顺序表销毁
}

设计思想:通过组合而非继承的方式复用顺序表功能,体现了"组合优于继承"的设计原则。

2. 联系人添加功能

复制代码
void ContactAdd(Contact* con)
{
    peoInfo info;
    printf("请输入要添加的联系人的姓名:\n");
    scanf("%s", info.name);
    
    printf("请输入要添加的联系人的性别:\n");
    scanf("%s", info.gender);
    
    printf("请输入要添加的联系人的年龄:\n");
    scanf("%d", &info.age);
    
    printf("请输入要添加的联系人的电话:\n");
    scanf("%s", info.tel);
    
    printf("请输入要添加的联系人的地址:\n");
    scanf("%s", info.addr);
    
    SLPushBack(con, info);  // 使用顺序表尾插
}

功能特点

  • 完整的用户交互流程

  • 逐字段输入,用户体验良好

  • 底层使用顺序表尾插,时间复杂度O(1)

3. 联系人查询与展示

按姓名查询辅助函数

复制代码
int FindByName(Contact* con, char name[])
{
    for (int i = 0; i < con->size; i++)
    {
        if (0 == strcmp(con->arr[i].name, name))
        {
            return i;  // 找到返回索引
        }
    }
    return -1;  // 未找到
}

联系人展示函数

复制代码
void ContactShow(Contact* con)
{
    printf("\n姓名\t\t性别\t\t年龄\t\t电话\t\t地址\n");
    for (int i = 0; i < con->size; i++)
    {
        printf("%s\t\t%s\t\t%d\t\t%s\t\t%s\n",
            con->arr[i].name, 
            con->arr[i].gender, 
            con->arr[i].age, 
            con->arr[i].tel, 
            con->arr[i].addr);
    }
}

算法分析

  • 查找操作时间复杂度:O(n)

  • 展示操作时间复杂度:O(n)

  • 使用制表符格式化输出,提升可读性

4. 联系人删除功能

复制代码
void ContactDel(Contact* con)
{
    char name[NAME_MAX];
    printf("请输入要删除的联系人姓名:\n");
    scanf("%s", name);
    
    int find = FindByName(con, name);
    if (find < 0)
    {
        printf("要删除的联系人数据不存在\n");
        return;
    }
    
    SLErase(con, find);  // 使用顺序表指定位置删除
    printf("删除成功!\n");
}

删除策略

  • 先查找确认存在性

  • 使用顺序表的指定位置删除功能

  • 提供用户反馈信息

5. 联系人修改功能

复制代码
void ContactModify(Contact* con)
{
    char name[NAME_MAX];
    printf("请输入要修改的联系人姓名:\n");
    scanf("%s", name);
    
    int find = FindByName(con, name);
    if (find < 0)
    {
        printf("要修改的联系人数据不存在\n");
        return;
    }
    
    // 重新输入所有信息
    printf("请输入新的联系人的姓名:\n");
    scanf("%s", con->arr[find].name);
    
    printf("请输入新的联系人的性别:\n");
    scanf("%s", con->arr[find].gender);
    
    printf("请输入新的联系人的年龄:\n");
    scanf("%d", &con->arr[find].age);
    
    printf("请输入新的联系人的电话:\n");
    scanf("%s", con->arr[find].tel);
    
    printf("请输入新的联系人的地址:\n");
    scanf("%s", con->arr[find].addr);
    
    printf("修改成功!\n");
}

设计考虑

  • 支持完整信息修改

  • 直接修改原数据,无需删除再添加

  • 保持与添加操作一致的用户体验

6. 用户界面设计

复制代码
void menu()
{
    printf("******************************************\n");
    printf("*********         通讯录         *********\n");
    printf("********1.添加联系人    2.删除联系人******\n");
    printf("********3.修改联系人    4.查找联系人******\n");
    printf("********5.展示联系人    0.退出通讯录******\n");
    printf("******************************************\n");
}

界面特点

  • 清晰的选项布局

  • 直观的操作指引

  • 美观的边框设计

测试代码分析

自动化测试函数

复制代码
void test()
{
    Contact con;
    ContactInit(&con);
    
    // 批量添加测试数据
    ContactAdd(&con);
    ContactAdd(&con);
    ContactAdd(&con);
    ContactAdd(&con);
    ContactAdd(&con);
    ContactShow(&con);

    // 测试删除功能
    ContactDel(&con);
    ContactShow(&con);

    // 测试修改功能
    ContactModify(&con);
    ContactShow(&con);
    
    // 测试查找功能
    ContactFind(&con);

    ContactDesTroy(&con);
}

交互式主程序

复制代码
int main()
{
    Contact con;
    ContactInit(&con);
    int n = 0;
    
    do
    {
        menu();
        printf("请输入你的选择:\n");
        scanf("%d", &n);
        switch (n)
        {
        case 1:
            ContactAdd(&con);
            break;
        case 2:
            ContactDel(&con);
            break;
        case 3:
            ContactModify(&con);
            break;
        case 4:
            ContactFind(&con);
            break;
        case 5:
            ContactShow(&con);
            break;
        case 0:
            printf("退出通讯录!\n");
            break;
        default:
            printf("输入错误,请重新输入:\n");
            break;
        }
    } while (n);
    
    ContactDesTroy(&con);
    return 0;
}

测试策略

  • 提供自动化测试和交互测试两种模式

  • 覆盖所有功能分支

  • 包含错误输入处理

顺序表底层实现分析

动态扩容机制

复制代码
void SLCheckCapacity(SL* ps)
{
    if (ps->capacity == ps->size)
    {
        int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        SLDataType* tmp = (SLDataType*)realloc(ps->arr, 
                             newCapacity * sizeof(SLDataType));
        if (tmp == NULL)
        {
            perror("realloc fail!");
            exit(1);
        }
        ps->arr = tmp;
        ps->capacity = newCapacity;
    }
}

扩容策略

  • 初始容量:4个元素

  • 扩容倍数:2倍

  • 使用realloc保持数据连续性

指定位置操作

复制代码
void SLInsert(SL* ps, int pos, SLDataType x)
{
    assert(ps);
    assert(pos >= 0 && pos <= ps->size);
    
    SLCheckCapacity(ps);
    for (int i = ps->size; i > pos; i--)
    {
        ps->arr[i] = ps->arr[i - 1];
    }
    ps->arr[pos] = x;
    ps->size++;
}

void SLErase(SL* ps, int pos)
{
    assert(ps);
    assert(pos >= 0 && pos < ps->size);

    for (int i = pos; i < ps->size - 1; i++)
    {
        ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;
}

算法复杂度

  • 插入:O(n)

  • 删除:O(n)

  • 需要移动元素是顺序表的固有特性

系统优势与局限性

优势

  1. 代码复用性强:通讯录完美复用了顺序表的功能

  2. 内存管理完善:自动扩容,防止内存泄漏

  3. 用户界面友好:清晰的菜单和操作指引

  4. 功能完整:覆盖通讯录基本操作需求

  5. 错误处理健全:输入验证和错误提示

局限性

  1. 查找效率:按姓名查找需要遍历,时间复杂度O(n)

  2. 数据持久化:未实现文件存储,数据无法保存

  3. 高级功能缺失:如分组、排序、导入导出等

  4. 输入验证不足:对用户输入数据的合法性检查不够完善

扩展建议

性能优化

  1. 添加索引:为姓名字段建立哈希索引,提升查找速度

  2. 实现排序:支持按不同字段排序显示

  3. 分页显示:联系人数量多时支持分页

功能增强

  1. 数据持久化:添加文件读写功能

  2. 高级搜索:支持多条件组合查询

  3. 数据导入导出:支持CSV等格式

  4. 重复检测:添加联系人时检查是否已存在

用户体验改进

  1. 输入验证:加强电话号码、邮箱等格式验证

  2. 批量操作:支持批量添加、删除

  3. 数据统计:显示联系人数量统计信息

总结

通过这个基于顺序表的通讯录系统实现,我们看到了数据结构理论在实际项目中的具体应用。顺序表虽然简单,但在合适的场景下能够提供良好的性能表现。

关键收获

  1. 抽象与复用:通过类型重定义和函数调用,实现了顺序表到通讯录的平滑过渡

  2. 完整项目体验:从数据结构设计到用户界面实现的完整开发流程

  3. 工程化思维:错误处理、内存管理、用户体验等工程化考虑

这个项目不仅展示了顺序表的应用,更重要的是体现了如何将一个复杂问题分解为多个简单模块,并通过组合现有组件来构建完整系统的软件开发思想。这种思维方式对于解决更大规模的软件工程问题具有重要指导意义。

对于学习者来说,理解这个项目的设计思路和实现细节,将为学习更复杂的数据结构和算法奠定坚实基础,同时培养解决实际问题的能力。

相关推荐
有所事事39 分钟前
【监控报警体系建设】系统自动添加所有出入口监控
后端
e***193541 分钟前
QoS质量配置
开发语言·智能路由器·php
雨落在了我的手上1 小时前
C语言入门(二十四):数据在内存中的存储
c语言
宠..1 小时前
使用纯代码设计界面
开发语言·c++·qt
froginwe111 小时前
SQL ALTER 语句详解
开发语言
.格子衫.1 小时前
028动态规划之字符串DP——算法备赛
算法·动态规划·字符串
小此方1 小时前
Re:从零开始的链式二叉树:建树、遍历、计数、查找、判全、销毁全链路实现与底层剖析
c语言·数据结构·c++·算法
狂奔小菜鸡1 小时前
Day21 | 枚举(Enum)与常见应用场景
java·后端·java ee
ALex_zry1 小时前
内核开发者的视角:C与Rust在系统编程中的哲学与实践
c语言·开发语言·rust