什么是约瑟夫问题
设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此 产生一个出队编号的序列
分析
1)创建一个尾辅助指针指向最后一个节点(头节点的前一个节点)
2)报数前根据输入的开始位置坐标进行移动firstNode头节点以及helperNode尾节点(移动输入次数-1次因为开始数的节点也会报数)
3)报数时根据输入的报数间隔进行移动firstNode节点以及helperNode尾节点(移动输入次数-1次因为开始数的节点也会报数)
4)这时的首节点则是出列节点,直接进行输出即可
5)当firstNode头节点等于helperNode尾节点时说明链表中只剩下最后一个元素直接进行出列即可
代码实现
/**
* 定义一个单向列表用于解决约瑟夫问题
* */
public class Node {
private int id;
private Node nextNode;
public Node(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Node getNextNode() {
return nextNode;
}
public void setNextNode(Node nextNode) {
this.nextNode = nextNode;
}
@Override
public String toString() {
return "Node{" +
"id=" + id+
'}';
}
}
/**
* 循环单向链表解决约瑟夫问题
*/
public class CyclicLinkedList {
Node first = null;
/**
* 增加nums个小朋友到循环队列里
* */
public void addBoy(int nums){
if(nums==0){
throw new RuntimeException("数量错误");
}
//辅助指针,帮助构建环形链表
Node lastNode = null;
for(int i = 1;i<=nums;i++){
Node node = new Node(i);
//如果是首个节点需要记录下来用于遍历
if(i==1){
first = node;
//自己也能进行循环
first.setNextNode(first);
//让辅助变量指向节点
lastNode = first;
}else{
//这里注意 lastNode可以直接新增因为lastNode = first这里还是指向同一个地址所以下面直接遍历first即可
lastNode.setNextNode(node);
//新增节点与头节点循环连接起来
node.setNextNode(first);
//让辅助变量指向最后节点
lastNode = node;
}
}
}
/**
* 遍历当前链表
* */
public void ergodic(){
Node node = first;
while(true){
if(node.getNextNode()==first){
break;
}
System.out.println(node.getId());
node = node.getNextNode();
}
System.out.println(node.getId());
}
/**
* 出圈操作
* @param start 开始的位置坐标
* @param num 每次间隔几个数字进行出圈
* */
public void OutNode(int start,int num){
if(first==null){
throw new RuntimeException("链表为空");
}
//第一步创建一个尾辅助指针指向最后一个节点
Node helperNode = first;
while(true){
if(helperNode.getNextNode()==first){
break;
}
helperNode = helperNode.getNextNode();
}
//根据输入的开始位置坐标进行移动firstNode节点以及helperNode尾节点
for(int c = 0;c<start-1;c++){
helperNode = helperNode.getNextNode();
first = first.getNextNode();
}
//出列操作
while(true){
//链表只剩下了头节点这时候直接输出即可
if(helperNode==first){
break;
}
//根据用户输入每次出列间隔个数进行出列操作
for(int k = 0;k<num-1;k++){
helperNode = helperNode.getNextNode();
first = first.getNextNode();
}
//当前头节点就是出列的节点
System.out.println("出列:"+first.getId());
//把头节点向后移动一位并把尾辅助节点的下一个节点指向头节点
first = first.getNextNode();
helperNode.setNextNode(first);
}
//输出最后一个节点
System.out.println("最后节点"+first.getId());
}
}
测试
public static void main(String[] args) {
CyclicLinkedList cyclicLinkedList = new CyclicLinkedList();
cyclicLinkedList.addBoy(5);
cyclicLinkedList.ergodic();
cyclicLinkedList.OutNode(1,2);
}