返回

双向链表与双向循环链表:从零开始理解数据结构

见解分享

双向链表介绍

双向链表是在单向链表的基础上增加了一个指向前面节点的指针 prev,使得链表中的每个节点都具有指向后继节点的 next 指针和指向前驱节点的 prev 指针。这样,双向链表就可以实现正向和反向的遍历,更加灵活地访问和修改数据。

双向链表的应用非常广泛,常见于需要进行正反向遍历的场景,例如:

  • 浏览器的前进和后退按钮
  • 文本编辑器中的撤销和重做功能
  • 操作系统中的进程管理
  • 虚拟内存管理
  • 文件系统中的索引

双向循环链表介绍

双向循环链表是在双向链表的基础上增加了一个指向头结点的指针 head,使得链表形成一个闭环。这样,双向循环链表可以实现无穷尽的遍历,并且在任何位置都可以进行插入和删除操作。

双向循环链表的应用也十分广泛,常见于需要进行循环遍历的场景,例如:

  • 音乐播放器中的播放列表
  • 游戏中的角色列表
  • 操作系统中的任务队列
  • 网络中的数据传输

双向链表与单向链表的比较

特点 单向链表 双向链表
指针 每个节点只有一个指向后继节点的指针 每个节点有两个指针,一个指向后继节点,一个指向前驱节点
遍历 只支持单向遍历 支持正向和反向遍历
访问和修改数据 相对困难,需要从头结点开始遍历 更加灵活,可以从任何位置访问和修改数据
应用场景 前进和后退按钮、撤销和重做功能 浏览器的前进和后退按钮、文本编辑器中的撤销和重做功能、操作系统中的进程管理、虚拟内存管理、文件系统中的索引

双向循环链表与双向链表的比较

特点 双向链表 双向循环链表
指针 每个节点有两个指针,一个指向后继节点,一个指向前驱节点 每个节点有两个指针,一个指向后继节点,一个指向前驱节点,还有一个指向头结点的指针
遍历 支持正向和反向遍历 支持无穷尽的遍历
插入和删除 可以从任何位置进行插入和删除 可以从任何位置进行插入和删除
应用场景 浏览器的前进和后退按钮、文本编辑器中的撤销和重做功能、操作系统中的进程管理、虚拟内存管理、文件系统中的索引 音乐播放器中的播放列表、游戏中的角色列表、操作系统中的任务队列、网络中的数据传输

双向链表和双向循环链表的实现

双向链表和双向循环链表的实现非常相似,主要区别在于双向循环链表在头结点和尾结点的处理上有所不同。

以下是用 C++ 实现的双向链表的代码:

class Node {
public:
    int data;
    Node* next;
    Node* prev;

    Node(int data) {
        this->data = data;
        this->next = nullptr;
        this->prev = nullptr;
    }
};

class DoublyLinkedList {
public:
    Node* head;
    Node* tail;

    DoublyLinkedList() {
        this->head = nullptr;
        this->tail = nullptr;
    }

    void insertAtHead(int data) {
        Node* newNode = new Node(data);
        if (this->head == nullptr) {
            this->head = newNode;
            this->tail = newNode;
        } else {
            newNode->next = this->head;
            this->head->prev = newNode;
            this->head = newNode;
        }
    }

    void insertAtTail(int data) {
        Node* newNode = new Node(data);
        if (this->tail == nullptr) {
            this->head = newNode;
            this->tail = newNode;
        } else {
            this->tail->next = newNode;
            newNode->prev = this->tail;
            this->tail = newNode;
        }
    }

    void deleteAtHead() {
        if (this->head == nullptr) {
            return;
        }

        if (this->head == this->tail) {
            delete this->head;
            this->head = nullptr;
            this->tail = nullptr;
        } else {
            Node* newHead = this->head->next;
            delete this->head;
            this->head = newHead;
            this->head->prev = nullptr;
        }
    }

    void deleteAtTail() {
        if (this->tail == nullptr) {
            return;
        }

        if (this->head == this->tail) {
            delete this->tail;
            this->head = nullptr;
            this->tail = nullptr;
        } else {
            Node* newTail = this->tail->prev;
            delete this->tail;
            this->tail = newTail;
            this->tail->next = nullptr;
        }
    }

    void printList() {
        Node* current = this->head;
        while (current != nullptr) {
            cout << current->data << " ";
            current = current->next;
        }
        cout << endl;
    }
};

以下是用 C++ 实现的双向循环链表的代码:

class Node {
public:
    int data;
    Node* next;
    Node* prev;

    Node(int data) {
        this->data = data;
        this->next = nullptr;
        this->prev = nullptr;
    }
};

class DoublyCircularLinkedList {
public:
    Node* head;

    DoublyCircularLinkedList() {
        this->head = nullptr;
    }

    void insertAtHead(int data) {
        Node* newNode = new Node(data);
        if (this->head == nullptr) {
            this->head = newNode;
            newNode->next = newNode;
            newNode->prev = newNode;
        } else {
            newNode->next = this->head;
            newNode->prev = this->head->prev;
            this->head->prev = newNode;
            this->head = newNode;