循环链表解决约瑟夫问题
前言
传说有这样一个故事,在罗马人占领乔塔帕特后,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,第一个人从1开始报数,依次往后,如果有人报数到3,那么这个人就必须自杀,然后再由他的下一个人重新从1开始报数,直到所有人都自杀身亡为止。然而约瑟夫和他的朋友并不想遵从。于是,约瑟夫要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,从而逃过了这场死亡游戏 。
一、循环链表
循环链表,顾名思义,链表整体要形成一个圆环状。在单向链表中,最后一个节点的指针为null,不指向任何结点,因为没有下一个元素了。要实现循环链表,我们只需要让单向链表的最后一个节点的指针指向头结点即可。
二、约瑟夫问题解决思路
根据前言可知,约瑟夫问题就是一个循环链表,我们只需要把每次自杀的人的位置编号打印出来,看最后是哪两个位置最后自杀,就可以让约瑟夫和它的朋友站在相应的位置,最后剩余他两,就可以逃之夭夭了,从此过上幸福美满的生活,十年之后…
-
约瑟夫问题梳理
41个人坐一圈,第一个人编号为1,第二个人编号为2,第n个人编号为n。
1.编号为1的人开始从1报数,依次向后,报数为3的那个人退出圈;
2.自退出那个人开始的下一个人再次从1开始报数,以此类推;
3.求出最后退出的那个人的编号。 -
代码实现
//约瑟夫问题
public class JosephTest {
public static void main(String[] args) {
//1. 创建循环链表
Node<Integer> first=null; //第一个节点
Node<Integer> curr=null; //当前节点
for (int i=1;i<=41;i++)
{
//如果是第一个节点
if(i==1)
{
first=new Node<>(i,null);
curr=first;
continue;
}
//如果是中间节点
Node<Integer> newNode = new Node<>(i, null);
curr.next=newNode;
curr=newNode;
//如果是最后一个节点
if(i==41)
{
curr.next=first;
}
}
//2. 遍历循环链表,完成自杀过程
curr=first; //遍历的当前节点
Node<Integer> curr_before=null; //当前节点的上一个节点
int count = 0; //计算器,模拟第几个人报数
//因为是循环链表,当一个节点的下一个节点是自己时(证明只是剩下一个人),则遍历结束
while (curr!=curr.next)
{
count++;
if(count==3)
{
//1.如果count等于3.打印并删除该节点,count重置为0
System.out.print(curr.item+",");
curr_before.next=curr.next;
curr=curr.next;
count=0;
}
else
{
//2.如果count不等于3,这移动当前节点
curr_before=curr;
curr=curr.next;
}
}
//打印最后一个数
System.out.print(curr.item);
}
private static class Node<T>
{
T item;
Node next;
public Node(T item, Node next)
{
this.item=item;
this.next=next;
}
}
}
- 运行结果