数据结构与算法之链表 链表约瑟夫环 递归公式推导

数据结构与算法阿木 发布于 2025-07-11 6 次阅读


链表约瑟夫环问题分析与递归公式推导

约瑟夫环问题是一个经典的算法问题,它起源于一个古老的传说。在这个问题中,N个人围成一圈,从第一个人开始报数,每数到M的人出列,然后从下一个人开始继续报数,直到所有人都出列。这个问题可以用多种数据结构来解决,其中链表是一种常用的数据结构。

本文将围绕链表约瑟夫环问题展开,首先介绍链表的基本概念,然后分析递归解法,并推导出递归公式,最后通过代码实现来验证我们的推导。

链表的基本概念

链表是一种线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。链表可以分为单链表、双向链表和循环链表等。

单链表

单链表是最简单的链表形式,每个节点包含数据和指向下一个节点的指针。以下是单链表节点的定义:

python

class ListNode:


def __init__(self, value=0, next=None):


self.value = value


self.next = next


循环链表

循环链表是一种特殊的链表,它的最后一个节点的指针指向链表的第一个节点,形成一个环。以下是循环链表节点的定义:

python

class CircularListNode:


def __init__(self, value=0, next=None):


self.value = value


self.next = next


约瑟夫环问题的递归解法

约瑟夫环问题的递归解法基于以下观察:

1. 当只有一个人时,无需报数,直接出列。

2. 当有N个人时,第M个人出列后,问题转化为N-1个人围成一圈,从第一个人开始报数,每数到M的人出列。

基于以上观察,我们可以得到递归解法的伪代码:


function josephus(n, m):


if n == 1:


return 0


else:


return (josephus(n - 1, m) + m) % n


其中,`josephus(n, m)`表示N个人围成一圈,每数到M的人出列的出列顺序。

递归公式推导

为了推导递归公式,我们假设当有N个人时,第M个人出列的顺序为F(N, M)。我们可以根据递归解法得到以下关系:


F(N, M) = (F(N - 1, M) + M) % N


其中,F(N, M)表示N个人围成一圈,每数到M的人出列的出列顺序。

为了方便推导,我们引入以下符号:

- F(N, M)表示N个人围成一圈,每数到M的人出列的出列顺序。

- F(N, M+1)表示N个人围成一圈,每数到M+1的人出列的出列顺序。

根据递归解法,我们可以得到以下关系:


F(N, M+1) = (F(N - 1, M+1) + M+1) % N


将F(N, M)和F(N, M+1)的关系联立,我们可以得到以下递推公式:


F(N, M+1) = (F(N - 1, M+1) + M+1) % N


F(N, M) = (F(N - 1, M) + M) % N


通过观察上述递推公式,我们可以发现,当M等于N-1时,F(N, M)和F(N, M+1)的关系变为:


F(N, N-1) = (F(N - 1, N-1) + N-1) % N


F(N, N) = (F(N - 1, N) + N) % N


由于F(N, N)表示只有一个人时的情况,即F(N, N) = 0,我们可以得到以下递归公式:


F(N, N-1) = (F(N - 1, N-1) + N-1) % N


F(N, N) = 0


通过递归计算,我们可以得到F(N, M)的值。

代码实现

下面是使用Python实现的约瑟夫环问题的代码:

python

class ListNode:


def __init__(self, value=0, next=None):


self.value = value


self.next = next

def create_circular_list(n):


head = ListNode(0)


current = head


for i in range(1, n+1):


current.next = ListNode(i)


current = current.next


current.next = head 形成环


return head

def josephus(n, m):


head = create_circular_list(n)


current = head


while n > 1:


for _ in range(m-1):


current = current.next


current.next = current.next.next


current = current.next


n -= 1


return current.value

测试代码


n = 7


m = 3


print(f"The last person to survive is: {josephus(n, m)}")


总结

本文介绍了链表约瑟夫环问题,分析了递归解法,并推导出了递归公式。通过代码实现,我们验证了我们的推导。在实际应用中,约瑟夫环问题可以用于解决一些实际问题,如资源分配、任务调度等。