数据结构 | Josephus问题
Josephus问题
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列,类似于丢手绢。
1.基于“数组”概念的解法
选择什么数据结构?为什么用数组?
基本思想:人推出了,但是保留其位置,将其位置标记为0,这样做的好处就是,保留了整个表的结构是不变的,处理起来很方便,代码简洁易懂
算法思路
- 通过数组,将N个人看成数组元素;
- 出局时元素设为0
- 循环报数时只将数组元素为1的元素计数
def josephus_A(n,k,m): People = list(range(1,n+1)) # 生成列表[1,2,...,n] i=k-1 # 从第k位同学开始,其索引为k-1 for num in range(n): count = 0 while count < m: if People[i]>0: # 此位置的同学还未推出 count += 1 if count == m: # 报数到m该同学推出 print(People[i],end="," if num<n-1 else "\n") People[i] =0 # 标记 i = (i+1) % n # i+1使之指向下一位同学;并且取模,当i>n循环完了一圈,从头新一轮 return josephus_A(10,5,6) ''' 10 6 3 1 9 2 5 4 8 7 '''
-
首先,既然是n个人,那么首先定义一个列表,里面元素是[1,2,3,4,...n],指定第k个人,他的下标就是k-1;
-
然后,既然是将n个人都找出来,每次必定会出来一个人,所以我们的for循环是n次;
-
在每找一个人的过程中又是经过m个人,所以,用while循环代替for循环,指定初始的count=0,所以while循环的总次数count不能超过m次,每一轮找到一个正常人(非0)就count加1,直到count==m,将这个位置的人赋值为0,不再参与下一轮游戏;
-
while循环里面,每结束一轮循环判断完一个人之后,就需要指向下一个同学即i=i+1,使列表的索引指向下一位同学。但是因为是个圆环,列表只有n个元素,所以赋值的同时取模做运算。分2种情况
-
当i+1小于n的时候,i就赋值为i+1,也就是下一个同学;
-
i>n时循环完了一圈,从头新一轮的筛选;
-
2.基于顺序表的解
把保存人员编号的list按表的方式处理,一旦确定了应该退出的人,就将表示其编号的元素从表中删除。这样随着计算的进行,所用的表将变的越来越短。用num表示表的长度,每退出一个人表的长度num就减一,直至表的长度为0时结束。采用这种想法和设计,表中的元素全是有效元素(不再出现表示没人的0),元素计数与下标计数得到统一,所以,下标更新可以用i=(i+m-1)%num统一表示。
def josephus_L(n,k,m): people = list(range(1,n+1)) i = k-1 for num in range(n,0,-1): i = (i+m-1)%num print(people.pop(i),end=" " if num>1 else "\n") josephus_L(10,5,6)
注意:else后面不需要写end="\n"
3.基于循环单链表的解
class Josephus(LCList): def turn(self,n): for i in range(n): self._rear = self._rear.next def __init__(self,n,k,m): LCList.__init__(self) for i in range(n): self.append(i+1) self.turn(k-1) for i in range(n): self.turn(m-1) print(self.pop(),end=", " if i != n-1 else "\n") Josephus(10,5,6)

浙公网安备 33010602011771号