📜  双链表 (1)

📅  最后修改于: 2023-12-03 15:07:22.946000             🧑  作者: Mango

双链表

简介

双链表是一种数据结构,它可以在任意位置插入/删除元素,并且可以双向遍历。相较于单链表,双链表需要记录前后两个链表节点,因此需要更多的内存空间。但是在某些情况下,使用双链表可以更加高效地解决问题。

定义

双链表由节点组成,每一个节点包含三个部分:前驱指针、后继指针和数据部分。其中,前驱指针指向前一个节点,后继指针指向下一个节点,数据部分存储节点所代表的数据。

template <typename T>
struct ListNode {
    T val;
    ListNode* prev;
    ListNode* next;
    ListNode(T x) : val(x), prev(nullptr), next(nullptr) {}
};
基本操作
插入节点

双链表可以在任意位置插入节点,具体过程如下:

  1. 新建一个节点。
  2. 将新节点的前驱指针指向插入位置的前一个节点。
  3. 将新节点的后继指针指向插入位置的后一个节点。
  4. 将插入位置前一个节点的后继指针指向新节点。
  5. 将插入位置后一个节点的前驱指针指向新节点。
template <typename T>
void insert(ListNode<T>*& head, ListNode<T>* node, T val) {
    ListNode<T>* new_node = new ListNode<T>(val);
    new_node->prev = node;
    new_node->next = node->next;
    node->next = new_node;
    if (new_node->next != nullptr) {
        new_node->next->prev = new_node;
    }
}
删除节点

删除节点需要先找到要删除的节点,具体过程如下:

  1. 找到要删除的节点。
  2. 将要删除节点的前驱指向要删除节点的后继。
  3. 将要删除节点的后继指向要删除节点的前驱。
  4. 释放要删除的节点空间。
template <typename T>
void remove(ListNode<T>*& head, ListNode<T>* node) {
    if (node == nullptr) {
        return;
    }
    if (node->prev != nullptr) {
        node->prev->next = node->next;
    } else {
        head = node->next;
    }
    if (node->next != nullptr) {
        node->next->prev = node->prev;
    }
    delete node;
}
遍历双链表

双链表可以双向遍历,具体过程如下:

  1. 从头节点开始,逐个访问每个节点的后继。
  2. 从尾节点开始,逐个访问每个节点的前驱。
template <typename T>
void traverse(ListNode<T>* head) {
    printf("from head to tail: ");
    for (ListNode<T>* p = head; p != nullptr; p = p->next) {
        printf("%d ", p->val);
    }
    printf("\n");

    printf("from tail to head: ");
    for (ListNode<T>* p = get_tail(head); p != nullptr; p = p->prev) {
        printf("%d ", p->val);
    }
    printf("\n");
}
应用场景

双链表可以优化一些需要大量随机插入和删除的场景,比如 LRU Cache 的实现。

总结

双链表是一种比较常用的数据结构,使用它可以高效地解决某些问题。对于程序员来说,学习双链表是必不可少的。