问题来源
约瑟夫问题是以弗拉维奥·约瑟夫命名的,他是1世纪的一名犹太历史学家,他在自己的日记中写道,他和他的40个战友被罗马军队包围在洞中。他们讨论是自杀还是被俘,最终决定自杀,并以抽签的方式决定杀掉谁,约瑟夫和另外一个人是最后留下的两个人。约瑟夫说服了那个人,他们将向罗马军队投降,不再自杀。约瑟夫把他的存活归因于运气或天意,他不知道是究竟是哪一个。
约瑟夫问题是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。
人们站在一个等待被处决的圈子里。 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行。 在跳过指定数量的人之后,处刑下一个人。 对剩下的人重复该过程,从下一个人开始,朝同一方向跳过相同数量的人,直到只剩下一个人,并被释放。
问题即,给定人数、起点、方向和要跳过的数字,选择初始圆圈中的位置以避免被处决。
问题描述
编号为1~N 的N个士兵站成一个圆圈(编号分别为1,2,3...N),从编号为k的士兵开始依次报数(1,2...m),数到m的士兵会出列,之后的士兵再从1开始报数。直到剩下最后一个士兵,求这个是士兵编号。
解法
比较简单的做法是用循环单链表模拟整个过程。如下图所示,由5个结点组成一圈,编号分别为1,2,3,4,5。
比如,从2号开始数3个数,即1,2,3,则4号出圈;
继续从5号开始数3个数,即1,2,3,则2号出圈;依次类推,最后一个结点为5号。
代码实现
定义一个头指针head和尾指针tail分别指向1号结点和5号结点,定义一个当前结点指向tail结点,首先找到开始报数的编号为k结点的前一个结点。继续向后遍历m次,找到出圈结点的前一个结点,这样就可以删除出圈结点,依次类推.....
public class JosephusCycle {
// 指向第一个结点
private Node first;
// 指向最后一个结点
private Node last;
public boolean add(Integer item) {
// 新创建一个结点
Node newNode = new Node(item, null);
// 判断是否为第一个结点,需要特殊处理
if(first == null && last == null) {
// 新结点的next指向自身
newNode.next = newNode;
first = newNode;
last = newNode;
}else {
newNode.next = last.next;
last.next = newNode;
last = newNode;
}
return true;
}
/**
* 约瑟夫问题
* eg.
* 1 2 3 4 5 从第2个结点开始从1数到3
* 依次出圈顺序为 4 2 1 3 5
* @param n 总结点数,每个结点都有一个编号,从1开始
* @param k 编号为k开始报数,1 2 3 ...
* @param m 数到m的结点出圈
*/
public void josephus(int n, int k, int m) {
if(n < 1) {
throw new IllegalArgumentException("结点数n不能小于1");
}
if(k > n) {
throw new IllegalArgumentException("参数k不能大于n");
}
for(int i = 1; i <= n; i++) {
this.add(i);
}
// 找到编号为k的前一个结点
Node tmp = last;
for(int j = 0; j < k-1; j++) {
tmp = tmp.next;
}
System.out.println(String.format("第%s个结点开始,向后数%s", tmp.next.item, m));
while (true) {
// 向后数到1...m
// 找到待删除的前一个结点
for(int i = 0; i < m-1; i++) {
tmp = tmp.next;
}
if(tmp.next == tmp) {
System.out.println("最后一个出圈的结点 = " + tmp.next.item);
break;
}
System.out.println("出圈的结点 = " + tmp.next.item);
// 删除结点
tmp.next = tmp.next.next;
}
}
private static class Node {
private Integer item;
private Node next;
public Node() {
}
public Node(Integer item, Node next) {
this.item = item;
this.next = next;
}
}
}
「更多精彩内容请关注公众号geekymv,喜欢请分享给更多的朋友哦」