Josephu(约瑟夫)问题
Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此 产生一个出队编号的序列。
- n = 6 一共有6人
- k = 2 从编号2的人开始从1报数
- m = 2 报数报到2的人出列
解题思路
环形单链表来完成
构建环形链表和节点(节点表示人)
- 首先创建第一个节点first,让first指向自己形成环形。
- 每添加新的节点,将新节点加入环形链表中。
根据用户输入的参数,实现人出列的
- 先将first节点定位到编号为k的节点。
- 使用辅助节点temp,指向first的前一个节点。
- 报数时first和temp移动(m-1)次
- 这是first就是需要出列的节点。
- temp指向first的后一个节点,实现出列。
- 等到节点只剩一个时,返回改节点完成!
代码实现
public class Solution {
public static void main(String[] args) {//测试
CircleSingleLinkedList list = new CircleSingleLinkedList();
list.addNode(5);
int no = list.exit(1, 2).no;
System.out.println(no);
}
}
class CircleSingleLinkedList{//创建环形单链表
private Node first = null;//第一个节点
/**
* 添加节点
* @param n 需要添加的节点数
*/
public void addNode(int n){
if (n < 1){//n 小于1 时不添加
return;
}
Node temp =null;//定义一个辅助节点,帮助构建环形链表
for (int i = 1; i<=n; i++){//循环添加
Node node = new Node(i);
if (i == 1){//添加第一个节点时next指向自己
first = node;
first.next = first;//自己指向自己
temp = first;//辅助节点指向第一个节点
}else {
temp.next = node;//添加新节点
node.next = first;//新节点指向第一个节点
temp = temp.next;//辅助节点后移(指向新节点)
}
}
}
public void show(){//链表打印
if (first == null){
return;
}
Node temp = first;
while (true){
System.out.println(temp.no);
if (temp.next == first){
break;
}
temp = temp.next;
}
}
/**
* 节点出列
* @param k 约定编号为 k(1<=k<=n)的人从 1 开始报数
* @param m 数到 m 的人出列
* @return 最后剩下的节点
*/
public Node exit(int k,int m){
//首先将first节点移动到编号为k的节点上
for (int i=2; i<=k;i++){//k==1就无需移动
first = first.next;
}
Node temp = null;//定义辅助节点,为了指向first的前一个节点
while (true){
for (int j = 1;j<=m-1; j++){//从first节点开始移动m-1次
temp= first;//将first节点赋给temp辅助节点
first = first.next;//first节点后移
}
//这时first节点为出列节点
first = first.next;//然后first节点后移
temp.next = first;//temp的next指向后移的first,完成出列
if (first.next ==first){//最后当first节点指向自己(剩下最后一个)
return first;//返回first节点
}
}
}
}
//创建节点类
class Node{
int no;//编号
Node next;//下一个节点
public Node(int no){
this.no = no;
}
}