C++ 单链表、双链表和循环链表

特性/操作单链表 (Singly Linked List)双链表 (Doubly Linked List)循环链表 (Circular Linked List)
节点结构数据 + 指向下一个节点指针数据 + 指向前一个节点指针 + 指向下一个节点指针数据 + 指向下一个节点指针
头指针指向第一个节点指向第一个节点指向第一个节点
遍历方向单向遍历双向遍历从头节点开始,循环遍历
插入操作在头部或尾部插入在头部或尾部插入在尾部插入,指向头节点
删除操作需要找到前一个节点直接访问前后节点需调整指针保持循环结构
内存管理节点删除需手动管理节点删除需手动管理节点删除需手动管理
应用场景动态数据存储,栈浏览器前进后退功能,音乐播放器游戏回合制,任务调度
优缺点- 内存利用高
- 插入/删除简单
- 难以逆向遍历
- 支持双向遍历
- 删除操作方便
- 内存开销大
- 节省空间
- 循环结构适合循环任务
- 删除操作需注意循环

操作示例

操作单链表例子双链表例子循环链表示例
插入insert(30)
头部: 30 -> 10 -> 20
insert(30)
头部: 30 <-> 10 <-> 20
insert(30)
尾部: 10 -> 20 -> 30 -> 头
删除remove(10)
结果: 20
remove(20)
结果: 10 <-> 30
remove(10)
结果: 20 -> 30 -> 头

一、单链表(Singly Linked List)

1. 定义

单链表是一种线性数据结构,由一系列节点构成。每个节点包含数据部分和指向下一个节点的指针,最后一个节点的指针指向 nullptr

2. 原理

  • 节点结构:每个节点包含数据和指向下一个节点的指针。
  • 头指针:链表的起始点,通过它访问其他节点。

3. 操作

  • 插入:在链表头部或尾部插入新节点。
  • 删除:根据值删除节点,需调整指针。
  • 遍历:从头节点开始,依次访问每个节点。

4. 应用场景

  • 动态数据存储(如购物车)。
  • 实现栈(后进先出)。

5. 示例代码

#include <iostream>

struct Node {
    int data;         // 节点数据
    Node* next;      // 指向下一个节点的指针
};

class SinglyLinkedList {
private:
    Node* head;      // 链表头指针

public:
    SinglyLinkedList() : head(nullptr) {} // 初始化头指针
    ~SinglyLinkedList();                    // 析构函数
    void insert(int value);                 // 插入节点
    void remove(int value);                 // 删除节点
    void display() const;                   // 显示链表内容
};

// 析构函数,释放链表内存
SinglyLinkedList::~SinglyLinkedList() {
    Node* current = head;
    while (current) {
        Node* next = current->next; // 保存下一个节点
        delete current;              // 删除当前节点
        current = next;              // 移动到下一个节点
    }
}

// 在链表头插入新节点
void SinglyLinkedList::insert(int value) {
    Node* newNode = new Node{value, head}; // 创建新节点
    head = newNode;                        // 更新头指针
}

// 删除指定值的节点
void SinglyLinkedList::remove(int value) {
    if (!head) return; // 链表为空直接返回
    if (head->data == value) { // 如果要删除的是头节点
        Node* temp = head;
        head = head->next; // 更新头指针
        delete temp;       // 删除节点
        return;
    }
    Node* current = head; // 当前节点
    while (current->next && current->next->data != value) {
        current = current->next; // 查找节点
    }
    if (current->next) { // 找到要删除的节点
        Node* temp = current->next;
        current->next = current->next->next; // 调整指针
        delete temp;                          // 删除节点
    }
}

// 显示链表内容
void SinglyLinkedList::display() const {
    Node* current = head; // 当前节点
    while (current) {
        std::cout << current->data << " "; // 输出节点数据
        current = current->next;            // 移动到下一个节点
    }
    std::cout << std::endl; // 输出换行
}

int main() {
    SinglyLinkedList sll; // 创建单链表实例
    sll.insert(10);       // 插入数据
    sll.insert(20);
    sll.display();        // 输出: 20 10
    sll.remove(10);      // 删除数据
    sll.display();        // 输出: 20
    return 0;             // 程序结束
}

插入操作

  • 在链表头插入

  • 插入 30
    原链表: 10 -> 20 -> nullptr
    插入后: 30 -> 10 -> 20 -> nullptr

  • 在链表尾插入

  • 插入 30
    原链表: 10 -> 20 -> nullptr
    插入后: 10 -> 20 -> 30 -> nullptr

删除操作

  • 删除头节点

  • 删除 10
    原链表: 10 -> 20 -> nullptr
    删除后: 20 -> nullptr

  • 删除中间节点

  • 删除 20
    原链表: 10 -> 20 -> 30 -> nullptr
    删除后: 10 -> 30 -> nullptr
     

二、双链表(Doubly Linked List)

1. 定义

双链表是一种线性数据结构,每个节点包含数据、指向下一个节点的指针和指向前一个节点的指针。

2. 原理

  • 节点结构:每个节点包含前后指针。
  • 双向遍历:可以从任一节点向前或向后遍历。

3. 操作

  • 插入:在链表头部或尾部插入新节点,需调整前后指针。
  • 删除:根据值删除节点,需调整前后指针。
  • 遍历:可以双向遍历链表。

4. 应用场景

  • 浏览器的前进和后退功能。
  • 音乐播放器的播放列表。

5. 示例代码

#include <iostream>

struct DNode {
    int data;         // 节点数据
    DNode* next;     // 指向下一个节点的指针
    DNode* prev;     // 指向前一个节点的指针
};

class DoublyLinkedList {
private:
    DNode* head;      // 链表头指针

public:
    DoublyLinkedList() : head(nullptr) {} // 初始化头指针
    ~DoublyLinkedList();                    // 析构函数
    void insert(int value);                 // 插入节点
    void remove(int value);                 // 删除节点
    void display() const;                   // 显示链表内容
};

// 析构函数,释放链表内存
DoublyLinkedList::~DoublyLinkedList() {
    DNode* current = head;
    while (current) {
        DNode* next = current->next; // 保存下一个节点
        delete current;               // 删除当前节点
        current = next;               // 移动到下一个节点
    }
}

// 在链表尾部插入新节点
void DoublyLinkedList::insert(int value) {
    DNode* newNode = new DNode{ value, nullptr, nullptr }; // 创建新节点
    if (!head) { // 链表为空
        head = newNode; // 更新头指针
        return;
    }
    DNode* temp = head; // 当前节点
    while (temp->next) {
        temp = temp->next; // 遍历到链表尾部
    }
    temp->next = newNode; // 将新节点连接到尾部
    newNode->prev = temp; // 更新新节点的前指针
}

// 删除指定值的节点
void DoublyLinkedList::remove(int value) {
    if (!head) return; // 链表为空直接返回
    if (head->data == value) { // 如果要删除的是头节点
        DNode* temp = head;
        head = head->next; // 更新头指针
        if (head) head->prev = nullptr; // 更新新头节点的前指针
        delete temp; // 删除节点
        return;
    }
    DNode* current = head; // 当前节点
    while (current && current->data != value) {
        current = current->next; // 查找节点
    }
    if (current) { // 找到要删除的节点
        if (current->next) current->next->prev = current->prev; // 更新后继节点的前指针
        if (current->prev) current->prev->next = current->next; // 更新前驱节点的后指针
        delete current; // 删除节点
    }
}

// 显示链表内容
void DoublyLinkedList::display() const {
    DNode* current = head; // 当前节点
    while (current) {
        std::cout << current->data << " "; // 输出节点数据
        current = current->next;            // 移动到下一个节点
    }
    std::cout << std::endl; // 输出换行
}

int main() {
    DoublyLinkedList dll; // 创建双链表实例
    dll.insert(1);        // 添加到播放列表
    dll.insert(2);
    dll.insert(3);
    dll.display();        // 输出: 1 2 3
    dll.remove(2);       // 从播放列表中移除
    dll.display();        // 输出: 1 3
    return 0;             // 程序结束
}

插入操作

  • 在链表头插入

  • 插入 30
    原链表: 10 <-> 20
    插入后: 30 <-> 10 <-> 20

  • 在链表尾插入

  • 插入 30
    原链表: 10 <-> 20
    插入后: 10 <-> 20 <-> 30

删除操作

  • 删除头节点

  • 删除 10
    原链表: 10 <-> 20
    删除后: 20

  • 删除中间节点

  • 删除 20
    原链表: 10 <-> 20 <-> 30
    删除后: 10 <-> 30

三、循环链表(Circular Linked List)

1. 定义

循环链表是一种特殊的链表,最后一个节点的指针指向头节点,形成一个循环结构。

2. 原理

  • 节点结构:与单链表相似,但最后一个节点的指针指向头节点。
  • 遍历:可以无限循环遍历链表。

3. 操作

  • 插入:在链表尾部插入新节点,需确保新节点指向头节点。
  • 删除:根据值删除节点,需调整指针以保持循环结构。
  • 遍历:从头节点开始,直到回到头节点。

4. 应用场景

  • 游戏中的回合制。
  • 任务调度。

5. 示例代码

#include <iostream>

struct CNode {
    int data;         // 节点数据
    CNode* next;     // 指向下一个节点的指针
};

class CircularLinkedList {
private:
    CNode* head;      // 链表头指针

public:
    CircularLinkedList() : head(nullptr) {} // 初始化头指针
    ~CircularLinkedList();                     // 析构函数
    void insert(int value);                    // 插入节点
    void remove(int value);                    // 删除节点
    void display() const;                      // 显示链表内容
};

// 析构函数,释放链表内存
CircularLinkedList::~CircularLinkedList() {
    if (!head) return; // 链表为空直接返回
    CNode* current = head; // 当前节点
    CNode* nextNode;        // 下一个节点
    do {
        nextNode = current->next; // 保存下一个节点
        delete current;           // 删除当前节点
        current = nextNode;       // 移动到下一个节点
    } while (current != head);    // 遍历至回到头节点
}

// 在链表尾部插入新节点
void CircularLinkedList::insert(int value) {
    CNode* newNode = new CNode{ value, nullptr }; // 创建新节点
    if (!head) { // 如果链表为空
        head = newNode; // 更新头指针
        newNode->next = head; // 新节点指向自身
        return;
    }
    CNode* temp = head; // 当前节点
    while (temp->next != head) { // 遍历到链表尾部
        temp = temp->next; // 移动到下一个节点
    }
    temp->next = newNode; // 将新节点连接到尾部
    newNode->next = head; // 新节点指向头节点,形成循环
}

// 删除指定值的节点
void CircularLinkedList::remove(int value) {
    if (!head) return; // 链表为空直接返回

    CNode* current = head; // 当前节点
    CNode* prev = nullptr; // 前驱节点

    if (head->data == value) { // 如果要删除的是头节点
        if (head->next == head) { // 如果链表只有一个节点
            delete head; // 删除节点
            head = nullptr; // 更新头指针
            return;
        }
        while (current->next != head) { // 找到尾节点
            current = current->next; // 移动到下一个节点
        }
        CNode* temp = head; // 保存头节点
        head = head->next; // 更新头指针
        current->next = head; // 更新尾节点的指针
        delete temp; // 删除节点
        return;
    }

    do {
        prev = current; // 记录前驱节点
        current = current->next; // 移动到下一个节点
    } while (current != head && current->data != value); // 查找值

    if (current != head) { // 找到要删除的节点
        prev->next = current->next; // 删除节点
        delete current; // 删除节点
    }
}

// 显示链表内容
void CircularLinkedList::display() const {
    if (!head) return; // 如果链表为空,直接返回

    CNode* current = head; // 当前节点
    do {
        std::cout << current->data << " "; // 输出节点数据
        current = current->next; // 移动到下一个节点
    } while (current != head); // 遍历至回到头节点
    std::cout << std::endl; // 输出换行
}

int main() {
    CircularLinkedList cll; // 创建循环链表实例
    cll.insert(1); // 模拟游戏回合
    cll.insert(2);
    cll.insert(3);
    cll.display(); // 输出: 1 2 3
    cll.remove(2); // 移除玩家 2
    cll.display(); // 输出: 1 3
    return 0; // 程序结束
}
 

插入操作

  • 在链表尾插入
  • 插入 30
    原链表: 10 -> 20 -> 头节点(10)
    插入后: 10 -> 20 -> 30 -> 头节点(10)

删除操作

  • 删除头节点

  • 删除 10
    原链表: 10 -> 20 -> 30 -> 头节点(10)
    删除后: 20 -> 30 -> 头节点(20)

  • 删除中间节点

  • 删除 20
    原链表: 10 -> 20 -> 30 -> 头节点(10)
    删除后: 10 -> 30 -> 头节点(10)
     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值