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日

相关文章

  • Scratch3.0初始化加载七牛云上的sbs文件的方法

    首先,Scratch3.0是一个非常流行的图形化编程工具,七牛云是一家云存储和内容分发网络服务商,为用户提供了方便快捷的云端存储服务。在Scratch3.0中,我们可以使用七牛云的存储空间来初始化加载sbs文件。以下是详细的攻略: 步骤1:在七牛云上创建存储空间 首先,你需要在七牛云上注册账号并且创建存储空间。创建存储空间时可以选择不同的存储区域、空间名称和…

    other 2023年6月20日
    00
  • centos下查看文件和文件夹大小

    CentOS下查看文件和文件夹大小 在CentOS操作系统中,我们经常需要安装和管理各种软件,这就需要我们对文件和文件夹进行大小的查看和统计。本文将介绍如何在CentOS下通过命令行的方式来查看文件和文件夹的大小。 查看单个文件大小 我们可以使用ls命令来查看文件的大小,它的格式是: ls -lh 文件名 其中,-lh选项表示以易读的方式显示文件大小,例如:…

    其他 2023年3月28日
    00
  • Android中RecyclerView布局代替GridView实现类似支付宝的界面

    Android中RecyclerView布局代替GridView实现类似支付宝的界面攻略 在Android中,我们可以使用RecyclerView布局来代替GridView,以实现类似支付宝的界面。RecyclerView是一个强大的列表控件,它提供了更好的性能和灵活性。 以下是实现该界面的完整攻略: 步骤1:添加依赖 首先,确保在项目的build.grad…

    other 2023年8月20日
    00
  • 三星手机黑屏无限重启解决方法

    三星手机黑屏无限重启解决方法 三星手机可能会出现黑屏、无限重启等问题,对用户的正常使用造成很大的困扰。下面介绍几种针对这些问题的解决方法。 1. 清除缓存再重启手机 在三星手机出现问题的情况下,清除缓存是第一步需要尝试的方法。因为缓存过多或者卡顿可能会导致设备出现问题,清除缓存能够释放更多的存储空间和内存,让设备更加流畅。具体步骤如下: 长按手机电源键,选择…

    other 2023年6月27日
    00
  • windows下es安装教程

    Windows下ES安装教程 Elasticsearch是一个高度可扩展的开源搜索与分析引擎,被广泛应用于日志分析、全文检索等应用场景中。本文将带领读者了解如何在Windows系统下安装和配置Elasticsearch。 前置条件 在进行ES安装前,需要确保以下环境已经准备完成: Java JDK 8 (推荐使用OpenJDK) 若您的电脑没有安装以上环境,…

    其他 2023年3月29日
    00
  • 遇到电脑关机慢、蓝屏、重启现象怎么办

    遇到电脑关机慢、蓝屏、重启现象怎么办 电脑出现关机慢、蓝屏和重启现象是很常见的问题,其原因可能非常复杂。在此,我们提供一些应对这种情况的解决方案。 方案一:检查电脑硬件问题 检查电脑内存,运行 Windows Memory Diagnostic 等内存测试软件,检测内存是否存在问题。如果发现内存故障,需要更换内存。 检查电脑硬盘状况,运行硬盘检测软件,如 D…

    other 2023年6月26日
    00
  • 关于java:如何动态地向string数组添加元素?

    Java中动态向String数组添加元素 在Java中,String数组是一种常见的数据类型,通常用于存储一组字符串。有时候,我们需要动态地向String数组添加元素,以便在运行时动态地扩展数组。本攻略将详细介绍如何在Java中动态地向String数组添加元素,包括两个示例说明。 使用ArrayList类 在Java中,ArrayList类是一种动态数组,可…

    other 2023年5月7日
    00
  • 迅雷怎么修改文件后缀名?迅雷重命名文件方法

    迅雷怎么修改文件后缀名?迅雷重命名文件方法攻略 迅雷是一款常用的下载工具,它提供了一种简便的方法来修改文件后缀名。下面是使用迅雷修改文件后缀名的完整攻略: 步骤一:打开迅雷软件 首先,确保你已经安装了迅雷软件,并且打开了它。 步骤二:选择要修改后缀名的文件 在迅雷软件中,找到你想要修改后缀名的文件。你可以通过在迅雷的下载列表中找到文件,或者通过导航到文件所在…

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