【算法】在Python中使用单链表

1. 单链表数据结构

单链表(Singly Linked List)是一种基础的数据结构,它由节点(Node)组成,每个节点包含两部分:数据域和指针域。每个节点存储一个元素,同时包含一个指向下一个节点的指针。这个指针指向链表中的下一个节点,形成了节点之间的链接。

一个单链表的最后一个节点的指针通常为空(或者称为 Nonenullnullptr,具体根据编程语言而定),表示链表的结束。

以下是单链表的主要特点和操作:

1.1 特点

  1. 动态内存分配: 单链表支持动态内存分配,可以在运行时灵活地增加或减少节点。
  2. 不需要连续内存空间: 节点在内存中可以是分散存储的,相比数组等需要连续内存的数据结构更加灵活。
  3. 插入和删除高效: 在链表中,插入和删除节点的操作相对高效,因为只需要改变指针的指向。

1.2 基本操作

  1. 创建链表: 初始化一个空链表。
  2. 插入节点: 在链表的指定位置或末尾插入新的节点。
  3. 删除节点: 从链表中删除指定位置或特定值的节点。
  4. 查找节点: 按照位置或值查找链表中的节点。
  5. 遍历链表: 从头节点开始,依次访问每个节点。
#!/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 如何检测单链表中的环?

检测单链表中是否存在环有多种方法,其中一种常见的方法是使用快慢指针。具体步骤如下:

  1. 定义两个指针,一个慢指针 slow,一个快指针 fast,初始时都指向链表的头节点。

  2. 使用循环,慢指针每次移动一步,快指针每次移动两步。

  3. 如果链表中存在环,那么快指针最终会追上慢指针,即它们会相遇。

  4. 如果链表中不存在环,那么快指针会先到达链表尾部,此时循环结束。

下面是一个检测单链表中是否存在环的示例代码:

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

你可能感兴趣的:(算法)