C++链表实现通讯录设计

本文将详细讲解如何使用C++语言实现一个基本的通讯录系统,该系统使用链表数据结构来保存联系人信息,并能够实现基本的增、删、查、改功能。本文的目标读者是具有一定C++基础的初学者。

实现思路

我们使用链表这种数据结构来存储联系人信息,每个节点表示一个联系人,可以存储该联系人的姓名、电话、住址等信息。每个节点不仅保存着联系人信息,还保存着指向前一个节点和后一个节点的指针,这样就可以在列表中轻松地插入和删除联系人了。

具体地,我们设计一个Person结构体来表示每个联系人,然后设计一个LinkedList类来表示整个通讯录系统。LinkedList类有以下几个成员函数:

  • void addPerson():添加一个新联系人;
  • void deletePerson():根据姓名删除联系人;
  • void searchPerson():根据姓名查找联系人;
  • void printList():打印整个通讯录;
  • void saveList():将通讯录保存到文件。

实现步骤

定义Person结构体

struct Person {
    string name;    // 姓名
    string phone;   // 电话
    string address; // 住址
    Person* prev;   // 前一个联系人的指针
    Person* next;   // 后一个联系人的指针
};

定义LinkedList类

class LinkedList {
public:
    LinkedList();
    ~LinkedList();
    void addPerson();
    void deletePerson();
    void searchPerson();
    void printList();
    void saveList();
private:
    void insertAtBegin(Person*);
    void insertAtEnd(Person*);
    void insertAtMiddle(Person*, Person*);
    void deleteNode(Person*);
    void deleteList();
    void sortList();
    Person* head;
};

其中,head变量表示链表的头指针。在类的构造函数中,将头指针初始化为空指针。

实现通讯录系统

首先,我们来完整地实现LinkedList类的定义。下面的代码中,我们省略了一部分具体实现,只给出了函数声明和注释,以便于直观地理解算法。

class LinkedList {
public:
    // 构造函数和析构函数
    LinkedList();
    ~LinkedList();

    // 添加、删除、查询联系人
    void addPerson();
    void deletePerson();
    void searchPerson();

    // 打印整个通讯录
    void printList();

    // 将通讯录保存到文件
    void saveList();

private:
    Person* head;

    // 在链表的头部插入联系人
    void insertAtBegin(Person*);

    // 在链表的尾部插入联系人
    void insertAtEnd(Person*);

    // 在链表的中间插入联系人
    void insertAtMiddle(Person*, Person*);

    // 删除一个联系人
    void deleteNode(Person*);

    // 删除整个通讯录
    void deleteList();

    // 对通讯录进行排序
    void sortList();
};

接下来,我们分别来实现各个函数。

1. 构造函数和析构函数
LinkedList::LinkedList() {
    head = nullptr;
}

LinkedList::~LinkedList() {
    deleteList();
}

在构造函数中,我们将头指针初始化为空指针。在析构函数中,我们调用了deleteList()函数来删除整个通讯录。

2. 添加联系人
void LinkedList::addPerson() {
    // 创建一个新的联系人
    Person* newPerson = new Person;
    cout << "请输入联系人姓名:";
    getline(cin, newPerson->name);
    cout << "请输入联系人电话号码:";
    getline(cin, newPerson->phone);
    cout << "请输入联系人地址:";
    getline(cin, newPerson->address);

    // 将新的联系人插入到链表中
    if (head == nullptr) {
        // 如果链表为空,则直接插入到头部
        insertAtBegin(newPerson);
    } else {
        // 否则,按照姓名进行排序,插入到合适的位置
        Person* curr = head;
        while (curr != nullptr && curr->name.compare(newPerson->name) < 0) {
            curr = curr->next;
        }
        if (curr == nullptr) {
            insertAtEnd(newPerson);
        } else {
            insertAtMiddle(newPerson, curr);
        }
    }

    cout << "联系人已经成功添加!" << endl;
}

addPerson()函数中,我们先让用户输入新联系人的信息,然后将该联系人插入到链表中。如果链表为空,那么我们直接将该联系人插入到头部;否则,我们按照姓名进行排序,找到该联系人应该插入的位置,然后插入到链表中。

注意,我们在这里重新定义了一个插入函数insertAtMiddle(),它可以在链表的中间插入联系人。

3. 删除联系人
void LinkedList::deletePerson() {
    // 让用户输入要删除的联系人的姓名
    string name;
    cout << "请输入要删除的联系人的姓名:";
    getline(cin, name);

    // 在链表中查找该联系人
    Person* curr = head;
    while (curr != nullptr && curr->name != name) {
        curr = curr->next;
    }

    // 如果找到该联系人,则删除它
    if (curr != nullptr) {
        deleteNode(curr);
        cout << "联系人已经成功删除!" << endl;
    } else {
        cout << "未找到该联系人!" << endl;
    }
}

deletePerson()函数中,我们让用户输入要删除的联系人的姓名,然后在链表中查找该联系人。如果找到了该联系人,那么我们调用deleteNode()函数来删除它。否则,我们就打印一条错误提示信息。

4. 查找联系人
void LinkedList::searchPerson() {
    // 让用户输入要查找的联系人的姓名
    string name;
    cout << "请输入要查找的联系人的姓名:";
    getline(cin, name);

    // 在链表中查找该联系人
    Person* curr = head;
    while (curr != nullptr && curr->name != name) {
        curr = curr->next;
    }

    // 如果找到了该联系人,则输出它的详细信息
    if (curr != nullptr) {
        cout << "姓名:" << curr->name << endl;
        cout << "电话:" << curr->phone << endl;
        cout << "地址:" << curr->address << endl;
    } else {
        cout << "未找到该联系人!" << endl;
    }
}

searchPerson()函数中,我们让用户输入要查找的联系人的姓名,然后在链表中查找该联系人。如果找到了该联系人,则输出它的详细信息。否则,我们打印一条错误提示信息。

5. 打印整个通讯录
void LinkedList::printList() {
    // 遍历整个链表并打印每个联系人的信息
    if (head == nullptr) {
        cout << "通讯录为空!" << endl;
    } else {
        Person* curr = head;
        while (curr != nullptr) {
            cout << "姓名:" << curr->name << endl;
            cout << "电话:" << curr->phone << endl;
            cout << "地址:" << curr->address << endl;
            cout << "----------------------" << endl;
            curr = curr->next;
        }
    }
}

printList()函数中,我们遍历整个链表并打印每个联系人的信息。如果链表为空,我们则打印一条错误提示信息。

6. 将通讯录保存到文件
void LinkedList::saveList() {
    // 让用户输入要保存的文件名
    string filename;
    cout << "请输入要保存的文件名:";
    getline(cin, filename);

    // 打开文件并写入通讯录内容
    ofstream outFile(filename);
    if (!outFile) {
        cout << "无法打开文件" << filename << ",保存失败!" << endl;
        return;
    }
    Person* curr = head;
    while (curr != nullptr) {
        outFile << curr->name << ','
                << curr->phone << ','
                << curr->address << '\n';
        curr = curr->next;
    }
    outFile.close();

    cout << "通讯录已成功保存到" << filename << "文件!" << endl;
}

saveList()函数中,我们让用户输入要保存的文件名,然后将通讯录内容写入到文件中。如果打开文件失败,我们则打印一条错误提示信息。

7. 在链表头部插入联系人
void LinkedList::insertAtBegin(Person* newPerson) {
    newPerson->prev = nullptr;
    newPerson->next = head;
    if (head != nullptr) {
        head->prev = newPerson;
    }
    head = newPerson;
}

insertAtBegin()函数中,我们将新联系人插入到链表头部。

8. 在链表尾部插入联系人
void LinkedList::insertAtEnd(Person* newPerson) {
    if (head == nullptr) {
        insertAtBegin(newPerson);
    } else {
        Person* curr = head;
        while (curr->next != nullptr) {
            curr = curr->next;
        }
        newPerson->prev = curr;
        curr->next = newPerson;
        newPerson->next = nullptr;
    }
}

insertAtEnd()函数中,我们将新联系人插入到链表尾部。如果链表为空,我们则将其插入到头部;否则,我们找到链表的尾部,并将新联系人插入到尾部。

9. 在链表中间插入联系人
void LinkedList::insertAtMiddle(Person* newPerson, Person* curr) {
    newPerson->prev = curr->prev;
    newPerson->next = curr;
    curr->prev->next = newPerson;
    curr->prev = newPerson;
}

insertAtMiddle()函数中,我们将新联系人插入到链表的中间位置。

10. 删除一个联系人
void LinkedList::deleteNode(Person* curr) {
    if (curr == head) {
        head = curr->next;
        if (head != nullptr) {
            head->prev = nullptr;
        }
    } else {
        curr->prev->next = curr->next;
        if (curr->next != nullptr) {
            curr->next->prev = curr->prev;
        }
    }
    delete curr;
}

deleteNode()函数中,我们删除一个联系人,并且处理链表的前后指针。

11. 删除整个通讯录
void LinkedList::deleteList() {
    Person* curr = head;
    while (curr != nullptr) {
        head = curr->next;
        delete curr;
        curr = head;
    }
}

deleteList()函数中,我们删除整个通讯录,并释放节点的内存。

12. 对通讯录进行排序

我们可以使用冒泡排序或快速排序来对通讯录进行排序。这里我们以冒泡排序为例。

void LinkedList::sortList() {
    if (head == nullptr) {
        return;
    }
    bool swapped = true;
    while (swapped) {
        swapped = false;
        Person* curr = head;
        while (curr->next != nullptr) {
            if (curr->name.compare(curr->next->name) > 0) {
                swap(curr, curr->next);
                swapped = true;
            }
            curr = curr->next;
        }
    }
}

void LinkedList::swap(Person* a, Person* b) {
    string tempName;
    string tempPhone;
    string tempAddress;
    tempName = a->name;
    tempPhone = a->phone;
    tempAddress = a->address;
    a->name = b->name;
    a->phone = b->phone;
    a->address = b->address;
    b->name = tempName;
    b->phone = tempPhone;
    b->address = tempAddress;
}

sortList()函数中,我们使用冒泡排序对通讯录按照姓名进行排序。在本例中,我们以姓名的字母顺序为排序依据。

我们还需要定义交换函数swap(),用来交换两个联系人的信息。

示例说明

下面,我们来演示一下这个通讯录程序的使用。

例1:添加联系人

我们依次添加一些联系人:

请输入联系人姓名:Mark
请输入联系人电话号码:123456
请输入联系人地址:Beijing
联系人已经成功添加!

请输入联系人姓名:Tom
请输入联系人电话号码:987654
请输入联系人地址:Shanghai
联系人已经成功添加!

请输入联系人姓名:Lily
请输入联系人电话号码:234567
请输入联系人地址:Guangzhou
联系人已经成功添加!
例2:查询联系人

我们查询一个联系人:

请输入要查找的联系人的姓名:Mark
姓名:Mark
电话:123456
地址:Beijing
例3:删除联系人

我们删除一个联系人:

请输入要删除的联系人的姓名:Tom
联系人已经成功删除!
例4:打印整个通讯录

我们打印整个通讯录:

姓名:Lily
电话:234567
地址:Guangzhou
----------------------
姓名:Mark
电话:123456
地址:Beijing
----------------------
例5:将通讯录存储到文件

我们将通讯录保存到文件data.txt

请输入要保存的文件名:data.txt
通讯录已成功保存到data.txt文件!
例6:从文件中读取通讯录

我们可以从文件中读取通讯录并载入内存。

```cpp

include

include

include

include "LinkedList.h"

using namespace std;

int main() {
LinkedList list;
ifstream inFile("data.txt");
if (!inFile) {
cout << "无法打开文件data.txt,程序将退出!" << endl;
return 1;
}
string line;
while (getline(inFile, line)) {
Person* newPerson = new Person;
string name, phone, address;
int pos1 = 0, pos2;
pos2 = line.find(',', pos1);
name = line.substr(pos1, pos2 - pos1);
pos1 = pos2 + 1;
pos2 = line.find(',', pos1);
phone = line.substr(pos1, pos2 - pos1);
pos1 = pos2 + 1;
address = line.substr(pos1);
newPerson->name = name;
newPerson->phone = phone;
newPerson->address = address;
list.insertAtEnd(newPerson);
}
inFile.close();
list.sortList();

while (true) {
    cout << "请选择功能:" << endl;
    cout << "1. 添加联系人" << endl;
    cout << "2. 删除联系人" << endl;
    cout << "3. 查找联系人" << endl;
    cout << "4. 打印整个通讯录" << endl;
    cout << "5. 保存通讯录到文件" << endl;
    cout << "0. 退出程序" << endl;
    cout << ">>> ";
    int choice;
    cin >> choice;
    cin.get();
    cout << endl;
    switch (choice) {
    case 1:
        list.addPerson();
        break;
    case 2:
        list.deletePerson();
        break;
    case 3:
        list.searchPerson();
        break;
    case 4:
        list.printList();
        break;
    case 5:
        list.saveList();
        break;
    case 0:
        return 0;
    default:
        cout << "无效的选择,请重新选择

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++链表实现通讯录设计 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 手机实际内存与标注内存不符是什么原因

    手机实际内存与标注内存不符的原因 当我们购买手机时,通常会看到手机的标注内存,比如64GB或128GB。然而,实际使用时,我们会发现手机的可用内存比标注内存要少。这是因为以下几个原因: 1. 操作系统和预装应用程序占用空间 手机内置的操作系统和预装的应用程序会占用一部分内存空间。这些应用程序可能包括系统应用、厂商自带应用和其他预装软件。这些应用程序和系统文件…

    other 2023年8月1日
    00
  • 4种方法缓解网络瓶颈问题 网络拥塞解决问题

    4种方法缓解网络瓶颈问题 网络拥塞解决问题 1. 增加带宽 增加带宽是缓解网络瓶颈问题的一种最常见的方法,可以通过升级网络设备、更换高速网络线缆、购买更高带宽的网络服务等方式来实现。这种方法可以有效地提高网络传输速度,从而避免网络拥塞的情况发生。 例如,某个企业内部的管理系统出现了卡顿现象,导致员工的工作效率受到了很大影响。经过排查发现,是因为该企业网络带宽…

    other 2023年6月26日
    00
  • xnconvert图片转换工具

    XnConvert图片转换工具的完整攻略 XnConvert是一款免费的图片转换工具,支持多种图片格式的转换和批量处理。本文将详细介绍XnConvert的使用方法,并提供两个示例说明以帮助您更好地了解和应用这个工具。 下载和安装 访问XnConvert官网(https://www.xnview.com/en/xnconvert/)。 点击“Download”…

    other 2023年5月7日
    00
  • SpringBoot深入浅出分析初始化器

    下面我来详细讲解一下“SpringBoot深入浅出分析初始化器”的完整攻略。 一、初始化器简介 Spring Boot 的启动器是分布式系统中常用的组件,初始化器则是启动器中的一种。初始化器通常是在 Spring Boot 应用程序启动前进行一些初始化操作并装配进容器,可以用来做自定义的初始化或者提供一些应用程序需要的共享资源等。 其中,初始化器是由 org…

    other 2023年6月20日
    00
  • 使命召唤12卡顿假死弹回桌面等问题的解决方法

    针对使命召唤12出现卡顿、假死、弹回桌面等问题,可以尝试以下几个解决方法: 方法一:修复游戏文件 这是一个常见的解决游戏问题的方法。可能是因为游戏文件缺失或被破坏,导致游戏出现问题。步骤如下: 打开Steam或Battle.net客户端,在游戏列表中找到使命召唤12,点击右键,选择“属性”或“选项”。 选择“局部文件”或“本地文件”,点击“验证游戏文件完整性…

    other 2023年6月27日
    00
  • java如何实现获取客户端ip地址的示例代码

    获取客户端IP地址是Java Web开发中常见的需求之一。下面是一份完整的攻略,包含了两个示例说明。 示例1:使用HttpServletRequest对象获取客户端IP地址 在Java Web开发中,可以使用HttpServletRequest对象来获取客户端IP地址。以下是一个示例代码: import javax.servlet.http.HttpServ…

    other 2023年7月31日
    00
  • Android实现Service重启的方法

    下面是详细讲解 Android 实现 Service 重启的方法的完整攻略。 什么是 Service 重启? Service 是 Android 中的一种组件,它可以在后台运行长时间的任务,即使应用退出或者被杀掉也能够继续运行。但是有时候,由于各种原因,Service 会被系统或者其他应用杀掉,这时候我们需要实现 Service 重启,让 Service 能…

    other 2023年6月27日
    00
  • win10怎么设置ip地址?win10配置静态IP地址

    Win10设置IP地址攻略 在Windows 10中,你可以通过以下步骤来设置IP地址和配置静态IP地址。 设置IP地址 打开“开始”菜单,点击“设置”图标(齿轮状图标)。 在“设置”窗口中,点击“网络和Internet”选项。 在左侧导航栏中,选择“以太网”或“Wi-Fi”,具体取决于你要设置的网络连接类型。 在右侧窗口中,找到你要配置的网络连接,点击该连…

    other 2023年7月30日
    00
合作推广
合作推广
分享本页
返回顶部