微信登录

链表 - 循环链表 - 循环链表的结构与使用

链表 - 循环链表 - 循环链表的结构与使用

一、引言

在数据结构的世界里,链表是一种基础且重要的线性数据结构。而循环链表作为链表的一种特殊形式,有着独特的结构和广泛的应用场景。本文将深入探讨循环链表的结构特点、实现方式以及实际应用,帮助大家更好地理解和运用这一数据结构。

二、链表基础回顾

在正式介绍循环链表之前,我们先来简单回顾一下普通链表的基本概念。链表是由一系列节点组成的数据结构,每个节点包含两部分:数据域和指针域。数据域用于存储实际的数据,而指针域则指向下一个节点的地址。通过这种方式,节点之间形成了一种链式的连接关系。

普通链表的最后一个节点的指针域通常指向 NULL,表示链表的结束。例如,一个简单的整数链表可能如下所示:

  1. [1] -> [2] -> [3] -> NULL

三、循环链表的结构特点

循环链表与普通链表的主要区别在于,它的最后一个节点的指针域不是指向 NULL,而是指向链表的第一个节点,从而形成一个封闭的循环。根据链表是否为单链表或双链表,循环链表也可以分为单向循环链表和双向循环链表。

1. 单向循环链表

单向循环链表中,每个节点只有一个指针域,指向下一个节点。最后一个节点的指针域指向头节点。其结构示意图如下:

  1. [1] -> [2] -> [3]
  2. ^ |
  3. |_____________|

2. 双向循环链表

双向循环链表中,每个节点有两个指针域,一个指向前一个节点,另一个指向后一个节点。头节点的前一个指针指向尾节点,尾节点的后一个指针指向头节点。其结构示意图如下:

  1. <-> [1] <-> [2] <-> [3] <->
  2. ^ |
  3. |______________________|

四、循环链表的实现

1. 单向循环链表的实现(Python 示例)

  1. # 定义节点类
  2. class Node:
  3. def __init__(self, data):
  4. self.data = data
  5. self.next = None
  6. # 定义单向循环链表类
  7. class CircularLinkedList:
  8. def __init__(self):
  9. self.head = None
  10. # 在链表尾部插入新节点
  11. def append(self, data):
  12. new_node = Node(data)
  13. if not self.head:
  14. self.head = new_node
  15. new_node.next = self.head
  16. else:
  17. current = self.head
  18. while current.next!= self.head:
  19. current = current.next
  20. current.next = new_node
  21. new_node.next = self.head
  22. # 遍历链表
  23. def display(self):
  24. if not self.head:
  25. return
  26. current = self.head
  27. while True:
  28. print(current.data, end=" ")
  29. current = current.next
  30. if current == self.head:
  31. break
  32. print()
  33. # 使用示例
  34. cll = CircularLinkedList()
  35. cll.append(1)
  36. cll.append(2)
  37. cll.append(3)
  38. cll.display() # 输出: 1 2 3

2. 双向循环链表的实现(Python 示例)

  1. # 定义双向节点类
  2. class DoubleNode:
  3. def __init__(self, data):
  4. self.data = data
  5. self.prev = None
  6. self.next = None
  7. # 定义双向循环链表类
  8. class DoublyCircularLinkedList:
  9. def __init__(self):
  10. self.head = None
  11. # 在链表尾部插入新节点
  12. def append(self, data):
  13. new_node = DoubleNode(data)
  14. if not self.head:
  15. self.head = new_node
  16. new_node.next = new_node
  17. new_node.prev = new_node
  18. else:
  19. last = self.head.prev
  20. last.next = new_node
  21. new_node.prev = last
  22. new_node.next = self.head
  23. self.head.prev = new_node
  24. # 遍历链表
  25. def display(self):
  26. if not self.head:
  27. return
  28. current = self.head
  29. while True:
  30. print(current.data, end=" ")
  31. current = current.next
  32. if current == self.head:
  33. break
  34. print()
  35. # 使用示例
  36. dccl = DoublyCircularLinkedList()
  37. dccl.append(1)
  38. dccl.append(2)
  39. dccl.append(3)
  40. dccl.display() # 输出: 1 2 3

五、循环链表的应用场景

1. 约瑟夫斯问题

约瑟夫斯问题是一个经典的问题,描述了 n 个人围成一圈,从某个人开始报数,报到 m 的人出列,然后从出列的下一个人重新开始报数,直到所有人都出列。使用循环链表可以很方便地解决这个问题。

  1. class Node:
  2. def __init__(self, data):
  3. self.data = data
  4. self.next = None
  5. def josephus_problem(n, m):
  6. # 创建循环链表
  7. head = Node(1)
  8. current = head
  9. for i in range(2, n + 1):
  10. new_node = Node(i)
  11. current.next = new_node
  12. current = new_node
  13. current.next = head
  14. # 开始报数
  15. prev = current
  16. current = head
  17. while current.next!= current:
  18. for i in range(m - 1):
  19. prev = current
  20. current = current.next
  21. print(f"出列的人是: {current.data}")
  22. prev.next = current.next
  23. current = prev.next
  24. print(f"最后剩下的人是: {current.data}")
  25. # 使用示例
  26. josephus_problem(5, 3)

2. 操作系统的进程调度

在操作系统中,进程调度算法有时会使用循环链表来管理多个进程。每个进程可以看作是链表中的一个节点,调度器按照循环链表的顺序依次选择进程执行,从而实现公平的进程调度。

六、总结

类型 结构特点 优点 缺点 应用场景
单向循环链表 每个节点只有一个指针域,最后一个节点指向头节点 实现简单,节省空间 只能单向遍历 约瑟夫斯问题等
双向循环链表 每个节点有两个指针域,头节点和尾节点相互指向 可以双向遍历,操作更灵活 占用更多空间,实现相对复杂 操作系统进程调度等

循环链表作为一种特殊的链表结构,在某些场景下具有独特的优势。通过本文的介绍,相信大家对循环链表的结构和使用有了更深入的了解。在实际应用中,可以根据具体的需求选择合适的链表类型,以提高程序的效率和性能。