返回

编程技术扫盲 --- 带头双向循环链表结构

后端

前言

前面学习了单链表的结构,并且做了些许单链表的 OJ 练习,相信大家已经对单链表的结构了如指掌。因此,本章带来了与单链表同源的但拥有不同的结构的链表 --- 带头双向循环链表供大家学习。

可能大家听到「带头」和「双向」就明白了,但是「循环」这个概念理解起来可能有点困难。本文将结合实例给大家详细讲解这一概念。

什么是「循环」链表?

环形队列是一种先进先出的队列,其中数组的最后一个元素链接到第一个元素,形成一个环路。当队列已满时,队列的队尾指向队头,形成一个环形结构。环形队列在某些场景下比普通队列更具优势,例如当队列需要不断地循环使用时。

双向循环链表也是如此,只不过它将双向链表的结构与环形队列的结构结合起来,形成了一个既可以双向遍历,又可以循环遍历的链表结构。

带头双向循环链表的结构

带头双向循环链表的结构与单链表非常相似,但它在每个节点中添加了两个额外的指针:prev 指针指向该节点的前一个节点,next 指针指向该节点的下一个节点。此外,带头双向循环链表还具有一个特殊的头节点,该节点不存储任何数据,但它将整个链表连接成一个环路。

带头双向循环链表的结构如下图所示:

+---+-----+-----+-----+-----+
|   |     |     |     |     |
+---+-----+-----+-----+-----+
|head| prev | data | next | null|
+---+-----+-----+-----+-----+
  • head:头节点,不存储任何数据,但它将整个链表连接成一个环路。
  • prev:指向该节点的前一个节点的指针。
  • data:存储该节点的数据。
  • next:指向该节点的下一个节点的指针。

带头双向循环链表的操作

带头双向循环链表的操作与单链表的操作非常相似,但它具有双向和循环的特点,因此在某些操作上有所不同。

1. 插入节点

在带头双向循环链表中插入节点,有以下几种情况:

  • 在头部插入节点:将新节点的 prev 指针指向头节点,将新节点的 next 指针指向头节点的 next 指针,将头节点的 next 指针指向新节点,将新节点的 next 指针指向头节点。
  • 在尾部插入节点:将新节点的 prev 指针指向尾节点,将新节点的 next 指针指向头节点,将尾节点的 next 指针指向新节点,将头节点的 prev 指针指向新节点。
  • 在中间插入节点:找到要插入节点的前一个节点,将新节点的 prev 指针指向该节点,将新节点的 next 指针指向该节点的 next 指针,将该节点的 next 指针指向新节点,将新节点的 prev 指针指向该节点。

2. 删除节点

在带头双向循环链表中删除节点,有以下几种情况:

  • 删除头节点:将头节点的 next 指针指向头节点的 next 指针的 next 指针,将头节点的 prev 指针指向头节点的 prev 指针的 prev 指针。
  • 删除尾节点:将尾节点的 prev 指针指向尾节点的 prev 指针的 prev 指针,将尾节点的 next 指针指向尾节点的 next 指针的 next 指针。
  • 删除中间节点:找到要删除节点的前一个节点和后一个节点,将该节点的前一个节点的 next 指针指向该节点的 next 指针,将该节点的 next 指针指向该节点的后一个节点,将该节点的后一个节点的 prev 指针指向该节点的 prev 指针。

3. 查找节点

在带头双向循环链表中查找节点,有以下几种情况:

  • 从头到尾查找:从头节点开始,依次遍历每个节点,直到找到要查找的节点。
  • 从尾到头查找:从尾节点开始,依次遍历每个节点,直到找到要查找的节点。

带头双向循环链表的应用

带头双向循环链表在某些场景下比单链表更具优势,例如:

  • 当需要对链表进行双向遍历时。
  • 当需要对链表进行循环遍历时。
  • 当需要对链表进行快速插入和删除操作时。

带头双向循环链表经常用于以下应用场景:

  • 浏览器历史记录:浏览器历史记录是一个带头双向循环链表,用户可以向前或向后浏览历史记录。
  • 操作系统进程队列:操作系统进程队列是一个带头双向循环链表,操作系统可以根据进程的优先级将进程插入或删除队列。
  • 虚拟内存管理:虚拟内存管理使用带头双向循环链表来管理内存页。

结语

带头双向循环链表是一种常见的数据结构,它与单链表类似,但具有双向和循环的特点。带头双向循环链表在某些场景下比单链表更具优势,例如当需要对链表进行双向遍历、循环遍历或快速插入和删除操作时。带头双向循环链表经常用于浏览器历史记录、操作系统进程队列和虚拟内存管理等应用场景。