josephus环问题是数据结构中一个常见的实例:假设有n个人做成一圈,现在要求从第k个人开始报数,报道m的人退出。然后从下一个人继续从头报数,并按相同的规则退出, 直到所有的退出。还有就是要按退出的顺序打印出出列人的编号。
其实看到这个“一圈”,我第一瞬间就想到了用循环链表,也确实可以。这里我分别中顺序表和循环链表进行了实现。
一、顺序表:
对于顺序表对josephus问题的实现由两种思路:
第一种:将出列人的位置设为None或0,不改变表的长度,这种实现比较复杂
代码如下:
# 自己想出来的
# list = list(range(1,101))
#
# k = 4 # 表示下标, 表示第五个人
# n = 0 # 总共选出的要出列的人数
# a_list = []
#
# for i in range(100): # 因为数组有一百个元素,每次打印一个的话要循环一百次
# x = 0 # 表示要数到的数
# for z in range(100):
# k = k % 100
# # 如果数到了100我们就将其值为0,从头开始
# if list[k] == None:
# # 如果下标为k的数是None,我们跳过这层循环。
# # 而且这个元素也不能用了,所以也要跳过这个元素
# k += 1
# continue
# if x == 49: # 如果数到了五十我们就退出
# # 从0到49刚好是50个数
# break
# k += 1
# x += 1
# a_list.append(list[k])
# list[k] = None
# n += 1
# if n == 50:
# break
# k += 1
# print(a_list, len(a_list), end='\n')
# 约瑟夫环问题的第一种解答。
def josephus_A(n, k, m):
people = list(range(1, n+1)) # 建立要给people的数组,值为1 ~ n
i = k - 1 # 下标
for num in range(n): # 循环n次,n为列表的长度,每次拿出一个元素最多要n次循环
count = 0
while count < m : # count用来计数的, 当它小于m(规定的次数)时
if people[i] > 0:
# 对于数到的人,都会将其位置值为0
count += 1
# 到这里还是会加一
if count == m:
print(people[i], end='')
people[i] = 0
# 如果count数到了人数,就直接可以打印了,然后将位置值为0
i = (i+1) % n
# 指着将i重新赋值给它的下一个下标
if num < n - 1:
print(", ", end="")
else:
print("")
return
josephus_A(100, 5, 10)
以上有两段代码,都是用来实现的
第二种思路:删除出列人的位置,用列表的pop方法就可以,这种方法改变了列表的长度。
代码如下:
# n表示开始时列表具有的长度
# k表示从第k个元素开始数起
# m表示间隔数加一
# num表示还剩余的列表长度
# def josephus_B(n, k, m):
# listIni = list(range(1, n+1))
# # 创建一个初始列表
# num = n
# # 浮动指针,num表示列表还剩余的长度
# # for i in range(n):
# while num > 1:
# k = (k + m-1) % num
# # 数到m个人,中间应该相隔m-1个人的
# print(listIni[k-1], end=", ")
# listIni.pop(k-1)
# num -= 1
# print(listIni[0], end="\n")
#
# josephus_B(100, 5, 10)
# 对于这么函数的算法复杂度的话,虽然函数体只有一层循环,但调用了pop方法,
# 这个方法的需要线性时间,所以算法复杂度死O(n2)
def josephus_L(n, k, m):
people = list(range(1, n+1))
num, i = n, k-1
# i表示下标
for num in range(n, 0, -1):
# 精妙之处:这里随着每次循环,num都会减一,即num表示的列表的长度减一
i = (i + m-1) % num
print(people.pop(i), end=(", " if num > 1 else "\n"))
# 用一个条件表达式确定一次输出的结束字符串
return
josephus_L(100, 5, 10)
二、链接表
使用链接表,当然是使用循环链接表来实现,也非常简单,用我们前面实现的循环链接表的类,当然再加一个对任意位置删除的方法
def pop(self, num):
# 指定位置的删除元素
if self.isEmpty() or self.kerear.nodenum < num:
# 如果链表时空的,或者指定删除的位置超过了链表的长度
# 产生一个错误
raise LinkedListUndeflow
pN = 0
p = self.head
q = None
while pN < num:
if num == 1:
print(self.popfirst(), end=", ")
return
if num == self.kerear.nodenum:
print(self.poplast(), end=", ")
return
q = p
p = p.next
pN += 1
if q:
q.next = p.next
print(p.elem, end=", ")
self.kerear.nodenum -= 1
就可以解决josephus环问题,,实现代码如下:
from 循环链表 import LinkedListUndeflow, LList
def josephus_C(n, k, m):
# 建立一个长度为100的循环链表
mlist1 = LList()
for i in range(100):
mlist1.append(i + 1)
for num in range(n, 0, -1):
k = (k + m-1) % num
mlist1.pop(k)
josephus_C(100, 5, 10)
以上就是josephus(约瑟夫)环问题的几种实现
免不了推荐一波裘宗燕老师的书《数据结构与算法 Python语言的实现》,反正无论对于学习python或是学习数据结构和算法,都有很大的作用。