题目描述
据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号。
题目分析:
本例可以使用链表(包括单向循环链表或者双向循环链表),数组来完成,具体可以根据自己需求或者熟悉哪一种。个人感觉循环链表更加形象,易于理解和操作。
代码实现:
package 链表;
public class Main {
public static void main(String[] args){
JosePhus josePhus = new JosePhus();
josePhus.getResult(39,3);
}
}
class InfoNode{
public int id;
public InfoNode next;
public InfoNode(int id){
this.id = id;
}
@Override
public String toString() {
return "InfoNode[" +
"id=" + id +
']';
}
}
class JosePhus{
public InfoNode start = null;
private void addBoyNum(int n) {
System.out.println("链表数据校验......");
if (n < 1 ){
System.out.println("n必须大于等于1");
return;
}
System.out.println("正在创建链表......");
InfoNode temp = null; //用于控制后续的添加操作
for (int i = 1; i <= n ; i++) {
InfoNode infoNode = new InfoNode(i);
if (i == 1){
start = infoNode; //给第一个节点赋值
start.next = start;//指向自己
temp = start; //给temp赋值以后 后续的增加节点操作就用它代替
}else {
temp.next = infoNode;
temp = temp.next;//然后temp后移
temp.next = start;//后移完成以后 再指向头部
}
}
System.out.println("环形链表创建完成~~~~");
}
public void getResult(int n, int m){
//1.先调用方法创建一个有n节点的环形链表
addBoyNum(n);
//2.校验数据是否正确
if (m > n || m > 1000 || m < 1){
System.out.println("输入的n或m不符合要求~~~~");
return;
}
InfoNode temp = start;
//3.出圈的时候要让temp始终位于start之前,所以接下来要让他先跑到最后一个位置
while (true){
if (temp.next == start){
break;
}
temp = temp.next;
}
//4.temp走到指定位置 开始出圈
while (true){
if (temp == start){
System.out.println("遍历结束!");
break;
}
for (int i = 1; i <= m-1 ; i++) {
start = start.next;
temp = temp.next;
}
System.out.println("出圈节点编号:"+start.id);
//去除出圈的节点
start = start.next;
temp.next = start;
}
//当退出整个循环以后 说明只剩一个节点,就是幸存的人
System.out.println("幸存者id是:"+start.id);
}
}
最后:
LeetCode自带的编译器结果不让过,但是idea里是没问题的,很懵......不知道这个是什么情况,还没玩懂怎么操作~~~~~蓝瘦