约瑟夫环问题描述:
编号为1,2,3...n的人一词围成一圈,从第k个人开始报数(从1开始),数到m的人退出。接着下一个人又从1开始报数,数到m的人退出,以此类推。问:剩下的人的编号是多少?
如:n=6,m=3,k=1
原始序列: 1 2 3 4 5 6 ; 从编号1开始报数
第一轮数完后的序列为: 1 2 4 5 ; 3、6出列——从编号1开始报数
第二轮数完后的序列为: 1 2 5 ; 4出列——从编号5开始报数
第三轮数完后的序列为: 1 5 ; 2 出列——从5开始报数
第四轮数完后的序列为: 1 ;5 出列
编号为1,2,3...n的人一词围成一圈,从第k个人开始报数(从1开始),数到m的人退出。接着下一个人又从1开始报数,数到m的人退出,以此类推。问:剩下的人的编号是多少?
如:n=6,m=3,k=1
原始序列: 1 2 3 4 5 6 ; 从编号1开始报数
第一轮数完后的序列为: 1 2 4 5 ; 3、6出列——从编号1开始报数
第二轮数完后的序列为: 1 2 5 ; 4出列——从编号5开始报数
第三轮数完后的序列为: 1 5 ; 2 出列——从5开始报数
第四轮数完后的序列为: 1 ;5 出列
所以,最后出列的编号为1.
求解方法一:模拟报数过程,时间复杂度O(M*N)
该方法的思想是将所有的编号构造成一个环形链表,然后移动到编号为k的节点开始报数,数到M的时候移除链表节点,接着从下一个节点开始报数,移除数到M的节点。以此类推,直到只剩下一个节点。
public static int josephusOMN(int n, int m) {
return josephusOMN(n, m, 1);
}
public static int josephusOMN(int n, int m, int k) {
if (n <= 0 || m <= 0 || k <= 0)
return -1;
// consruct josephus circle
JosephusNode header = new JosephusNode(1);
JosephusNode nodes = header;
for (int i = 2; i <= n; i++) {
nodes.mNext = new JosephusNode(i);
nodes = nodes.mNext;
}
nodes.mNext = header;
// end
for (int i = 1; i < k; i++) {
nodes = nodes.mNext; // move to start off person
}
while (nodes != nodes.mNext) {
for (int i = 1; i < m; i++) {
nodes = nodes.mNext;
}
// System.out.print(nodes.mNext.mNum + " , ");
nodes.mNext = nodes.mNext.mNext;
}
return nodes.mNum;
}
private static class JosephusNode {
public int mNum;
public JosephusNode mNext;
public JosephusNode(int num) {
this(num, null);
}
public JosephusNode(int num, JosephusNode node) {
mNum = num;
mNext = node;
}
}
求解方法二:方便求解最后的胜利者同时也适合打印 出列 序列的,时间复杂度O(N).
下面方法借鉴自Java程序练习-约瑟夫环问题
public static int josepusONWithList(int n, int m) {
return josepusONWithList(n, m, 1);
}
public static int josepusONWithList(int n, int m, int k) {
if (n <= 0 || m <= 0 || k <= 0)
return -1;
LinkedList<Integer> list = new LinkedList<Integer>();
for (int i = 1; i <= n; i++)
list.add(i);
int outPos;
while (list.size() > 1) {
outPos = (int) (k + m - 2) % list.size();
// System.out.print(list.get(outPos)+" , ");
list.remove(outPos);
k = outPos + 1;
}
// System.out.println(list.get(0));
return list.get(0);
}
求解方法三:对模拟方法的改进——数学优化方法,时间复杂度O(N)。具体思想参见约瑟夫环的数学优化方法
public static int josephusON(int n, int m) {
return josephusON(n, m, 1);
}
/**
* Josephus 环的一个O(N)算法
*
* @param n 总人数
* @param m 数到m的人出列
* @param k 开始报数人的编号
* @return 最后一个出列的编号
*/
public static int josephusON(int n, int m, int k) {
int p = 0;
for (int i = 2; i <= n; i++) {
// System.out.print((p + k == n ? n : (p + k) % n)+" ");
p = (p + m) % i;
}
return p + k == n ? n : (p + k) % n; // 返回最后一人的位置
}
在有时候需要知道出列的序列,上述System.out部分就是出列序列。
约瑟夫环问题求解就先写到这里,朋友们若有其他方法也别忘了告诉楼主哦^_^... 收集到其他求解方法后再补充。