1. 单链表数据结构
单链表(Singly Linked List)是一种基础的数据结构,它由节点(Node)组成,每个节点包含两部分:数据域和指针域。每个节点存储一个元素,同时包含一个指向下一个节点的指针。这个指针指向链表中的下一个节点,形成了节点之间的链接。
一个单链表的最后一个节点的指针通常为空(或者称为 None
、null
或 nullptr
,具体根据编程语言而定),表示链表的结束。
以下是单链表的主要特点和操作:
1.1 特点
- 动态内存分配: 单链表支持动态内存分配,可以在运行时灵活地增加或减少节点。
- 不需要连续内存空间: 节点在内存中可以是分散存储的,相比数组等需要连续内存的数据结构更加灵活。
- 插入和删除高效: 在链表中,插入和删除节点的操作相对高效,因为只需要改变指针的指向。
1.2 基本操作
- 创建链表: 初始化一个空链表。
- 插入节点: 在链表的指定位置或末尾插入新的节点。
- 删除节点: 从链表中删除指定位置或特定值的节点。
- 查找节点: 按照位置或值查找链表中的节点。
- 遍历链表: 从头节点开始,依次访问每个节点。
#!/usr/bin/env python
# coding=utf-8
# @Time : 2024/1/26 00:13
# @Software: PyCharm
class Node:
def __init__(self, data=None):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
current = self.head
while current.next:
current = current.next
current.next = new_node
def prepend(self, data):
new_node = Node(data)
new_node.next = self.head
self.head = new_node
def delete(self, data):
if not self.head:
return
if self.head.data == data:
self.head = self.head.next
return
current = self.head
while current.next and current.next.data != data:
current = current.next
if current.next:
current.next = current.next.next
def reverse(self):
if self.head is None or self.head.next is None:
return self.head
pre = None
cur = self.head
while cur:
tmp = cur.next # 记录下一个
cur.next = pre # 当前指向前一个
pre = cur # 移动两个游标
cur = tmp
self.head = pre
def display(self):
elements = []
current = self.head
while current:
elements.append(current.data)
current = current.next
print(" -> ".join(map(str, elements)))
def test():
# 示例用法
linked_list = LinkedList()
linked_list.append(1)
linked_list.append(2)
linked_list.append(3)
linked_list.display() # 输出: 1 -> 2 -> 3
linked_list.prepend(0)
linked_list.display() # 输出: 0 -> 1 -> 2 -> 3
linked_list.delete(2)
linked_list.display() # 输出: 0 -> 1 -> 3
linked_list.reverse()
linked_list.display()
if __name__ == '__main__':
test()
2. 单链表中的环
什么是单链表中的环?
在单链表中,如果链表的某个节点的 next
指针指向之前已经遍历过的节点,形成了一个闭环,那么链表就包含了一个环。这种环状结构也被称为循环链表。
在正常的软件开发和数据结构设计中,单链表中的环是一个异常情况,通常是由于错误或异常造成的。环的存在通常是需要被排除或修复的问题,因为它可能导致不可预测的行为,如无限循环、死锁等。在正规的软件开发中,单链表中的环是应该被避免的,因为它会引入不稳定性和不可预测性。在实际应用中,我们更倾向于构建清晰、稳定和可靠的数据结构,以提高代码的可维护性和可读性。
2.1 如何检测单链表中的环?
检测单链表中是否存在环有多种方法,其中一种常见的方法是使用快慢指针。具体步骤如下:
-
定义两个指针,一个慢指针
slow
,一个快指针fast
,初始时都指向链表的头节点。 -
使用循环,慢指针每次移动一步,快指针每次移动两步。
-
如果链表中存在环,那么快指针最终会追上慢指针,即它们会相遇。
-
如果链表中不存在环,那么快指针会先到达链表尾部,此时循环结束。
下面是一个检测单链表中是否存在环的示例代码:
class ListNode:
def __init__(self, value=0, next=None):
self.value = value
self.next = next
def has_cycle(head):
if not head or not head.next:
return False
slow = head
fast = head.next
while fast and fast.next:
if slow == fast:
return True
slow = slow.next
fast = fast.next.next
return False
# 示例用法
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node2 # 创建环,node4指向node2
print(has_cycle(node1)) # 输出: True
2.2 使用单链表中环的巧妙算法
然而,有时候在算法和编程竞赛中,为了解决一些特定问题,人们可能会考虑在链表中构造环来利用环的性质。这可能包括一些巧妙的算法,但这种做法通常是临时性的,特定问题的解决方案,并不适用于一般的软件开发场景。
class Node:
def __init__(self, value):
self.value = value
self.next = None
def josephus(n, m):
# 构建循环链表
head = Node(1)
current = head
for i in range(2, n + 1):
current.next = Node(i)
current = current.next
current.next = head # 形成环
# 模拟报数并删除节点
current = head
while current.next != current:
for _ in range(m - 1):
current = current.next
current.value = current.next.value # 将下一个节点的值赋给当前节点
current.next = current.next.next
return current.value
# 示例用法
n = 7
m = 3
result = josephus(n, m)
print("最后剩下的人的编号:", result) # 输出: 4