📜  数据结构-双链表(1)

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

数据结构-双链表

双链表(Doubly linked list)是一种使用了两个指向相邻节点的指针的链表数据结构。与单链表不同的是,双链表中的每个节点除了有一个指向下一个节点的指针以外,还有一个指向前一个节点的指针。这样除了可以向前遍历,也能向后遍历。

双链表的定义
template <typename T>
struct DListNode {
  T data;
  DListNode* prev;
  DListNode* next;
  DListNode(T data = T{}, DListNode* prev = nullptr, DListNode* next = nullptr)
      : data(data), prev(prev), next(next) {}
};

template <typename T>
class DoublyLinkedList {
public:
  DoublyLinkedList() : head_(new DListNode<T>()), tail_(new DListNode<T>()) {
    head_->next = tail_;
    tail_->prev = head_;
  }

  // 头插法
  void PushFront(const T& data) {
    DListNode<T>* node = new DListNode<T>(data, head_, head_->next);
    head_->next->prev = node;
    head_->next = node;
  }

  // 尾插法
  void PushBack(const T& data) {
    DListNode<T>* node = new DListNode<T>(data, tail_->prev, tail_);
    tail_->prev->next = node;
    tail_->prev = node;
  }

  // 删除节点
  void Remove(DListNode<T>* node) {
    node->prev->next = node->next;
    node->next->prev = node->prev;
    delete node;
  }

  // 清空链表
  void Clear() {
    DListNode<T>* cur = head_->next;
    while (cur != tail_) {
      DListNode<T>* del = cur;
      cur = cur->next;
      delete del;
    }
    // 重新初始化头节点和尾节点,指向彼此
    head_->next = tail_;
    tail_->prev = head_;
  }

  // 获取链表长度
  int Size() const {
    int count = 0;
    DListNode<T>* cur = head_->next;
    while (cur != tail_) {
      ++count;
      cur = cur->next;
    }
    return count;
  }

  // 判断是否为空
  bool Empty() const { return head_->next == tail_; }

  // 获取第 k 个节点的数据,k 取值从 0 开始
  T& Get(int k) const {
    DListNode<T>* cur = head_->next;
    while (k-- && cur != tail_) {
      cur = cur->next;
    }
    return cur->data;
  }

  // 获取第一个节点
  T& Front() const { return head_->next->data; }

  // 获取最后一个节点
  T& Back() const { return tail_->prev->data; }

  // 打印链表
  void Print() const {
    DListNode<T>* cur = head_->next;
    while (cur != tail_) {
      std::cout << cur->data << ' ';
      cur = cur->next;
    }
    std::cout << '\n';
  }

private:
  DListNode<T>* head_; // 头节点,指向链表的头部哨兵节点
  DListNode<T>* tail_; // 尾节点,指向链表的尾部哨兵节点
};
双链表的操作
头插法和尾插法
#include "doubly_linked_list.h"

int main() {
  DoublyLinkedList<int> list;

  // 头插法插入元素,结果为 5 4 3 2 1
  for (int i = 1; i <= 5; ++i) {
    list.PushFront(i);
  }
  list.Print();

  // 尾插法插入元素,结果为 5 4 3 2 1 6 7 8 9 10
  for (int i = 6; i <= 10; ++i) {
    list.PushBack(i);
  }
  list.Print();

  return 0;
}
删除节点
#include "doubly_linked_list.h"

int main() {
  DoublyLinkedList<int> list;

  // 头插法插入元素,结果为 5 4 3 2 1
  for (int i = 1; i <= 5; ++i) {
    list.PushFront(i);
  }
  list.Print();

  // 删除第 3 个节点,结果为 5 4 2 1
  list.Remove(list.Get(2));
  list.Print();

  // 删除第 1 个节点,结果为 4 2 1
  list.Remove(list.Get(0));
  list.Print();

  // 删除最后一个节点,结果为 4 2
  list.Remove(list.Get(1));
  list.Print();

  return 0;
}
清空链表
#include "doubly_linked_list.h"

int main() {
  DoublyLinkedList<int> list;

  // 头插法插入元素,结果为 5 4 3 2 1
  for (int i = 1; i <= 5; ++i) {
    list.PushFront(i);
  }
  list.Print();

  // 清空链表
  list.Clear();
  std::cout << "链表已清空,长度为:" << list.Size() << '\n';

  return 0;
}
获取链表长度
#include "doubly_linked_list.h"

int main() {
  DoublyLinkedList<int> list;

  // 头插法插入元素,结果为 5 4 3 2 1
  for (int i = 1; i <= 5; ++i) {
    list.PushFront(i);
  }
  list.Print();

  // 链表长度为 5
  std::cout << "链表长度为:" << list.Size() << '\n';

  return 0;
}
判断是否为空
#include "doubly_linked_list.h"

int main() {
  DoublyLinkedList<int> list;

  // 判断链表是否为空,结果为 true
  if (list.Empty()) {
    std::cout << "链表为空\n";
  }

  // 头插法插入元素,结果为 5 4 3 2 1
  for (int i = 1; i <= 5; ++i) {
    list.PushFront(i);
  }
  list.Print();

  // 判断链表是否为空,结果为 false
  if (!list.Empty()) {
    std::cout << "链表不为空\n";
  }

  return 0;
}
获取节点数据
#include "doubly_linked_list.h"

int main() {
  DoublyLinkedList<int> list;

  // 头插法插入元素,结果为 5 4 3 2 1
  for (int i = 1; i <= 5; ++i) {
    list.PushFront(i);
  }
  list.Print();

  // 获取第 2 个节点的数据,结果为 4
  std::cout << "第 2 个节点的数据为:" << list.Get(1) << '\n';

  // 获取第 5 个节点的数据,结果为 1
  std::cout << "第 5 个节点的数据为:" << list.Get(4) << '\n';

  return 0;
}
获取第一个节点和最后一个节点
#include "doubly_linked_list.h"

int main() {
  DoublyLinkedList<int> list;

  // 头插法插入元素,结果为 5 4 3 2 1
  for (int i = 1; i <= 5; ++i) {
    list.PushFront(i);
  }
  list.Print();

  // 获取第一个节点的数据,结果为 5
  std::cout << "第一个节点的数据为:" << list.Front() << '\n';

  // 获取最后一个节点的数据,结果为 1
  std::cout << "最后一个节点的数据为:" << list.Back() << '\n';

  return 0;
}