约瑟夫问题:
场景:
一堆猴子都有编号,编号是1,2,3 …m,这群猴子(m个)按照1-m的顺序围坐一圈,
从第1开始数,每数到第N个,该猴子就要离开此圈,这样依次下来,
直到圈中只剩下最后一只猴子,则该猴子为大王。
解决方法:单向环形链表或数组(取模,就像环形队列),下面解决的方法为单向环形链表
解决思路:
用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。
本文代码流程:
- 定义描述约瑟夫环节点的类,该节点中包含节点的data域:索引no,该节点的next域:next
- 定义解决约瑟夫环问题的类,该类中定义约瑟夫环的首节点first,类中有创建约瑟夫环方法,打印约瑟夫环方法,约瑟夫环出圈顺序方法
- 创建约瑟夫环方法:参数是约瑟夫环节点个数num,方法中curr为临时节点,表示为当前所在节点
- 出圈方法:参数是游戏开始索引startIndex,一轮游戏经过的人数k,约瑟夫环的节点个数num,
- 首先创建约瑟夫环
- 定位到游戏指定的开始节点
- 开始循环出圈
- 获得出圈顺序数组outQueue
1.描述约瑟夫环节点的类
/*约瑟夫环节点*/
class JRPNode{
/*no(索引)*/
private int no;
/*next*/
private JRPNode next;
public JRPNode() {
}
public JRPNode(int no) {
this.no = no;
}
public JRPNode( int no, JRPNode next) {
this.no = no;
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public JRPNode getNext() {
return next;
}
public void setNext(JRPNode next) {
this.next = next;
}
@Override
public String toString() {
return "JRPNode{" +
"no=" + no +
'}';
}
}
2.解决约瑟夫环问题的类
public class JosephRingProblem{
/*创建的第一个节点*/
private static JRPNode first=null;
3.创建约瑟夫环
/**
* 方法描述
* @param: [num]
* @return: void
* @author: zh
* @date: 2021/7/14
* 创建约瑟夫环(指定节点个数)
*/
public static void addNode(int num){
if (num<1){
System.out.println("无法创建约瑟夫环!");
return;
}
/*记录当前节点*/
JRPNode curr=new JRPNode();
for (int i = 1; i <= num; i++) {
JRPNode node=new JRPNode(i) ;
/*当是第一个节点时,需要自己连自己形成一个环状*/
if (i==1){
/*初始化首节点*/
first=node;
/*因为只有首节点,所以将首节点的next域指向首节点形成环装*/
first.setNext(first);
/*设置当前节点为首节点*/
curr=first;
}else {
/*由于第一次当前的节点指向first节点,即就是从first->first ---->first->node first(即断了环)*/
curr.setNext(node);
/*设置当前节点为新添加的节点*/
curr=node;
/*当前节点最终指向首节点,形成环first->node->first*/
curr.setNext(first);
}
}
}
4.实现约瑟夫环出圈
/**
* 方法描述
* @param: [startIndex, k, num]
* @return: int
* @author: zh
* @date: 2021/7/14
* 约瑟夫环出圈(开始索引,一轮经过节点的个数,约瑟夫环节点的个数)
*/
public int[] outCircle(int startIndex, int k,int num){
if (k<1){
System.out.println("k值有问题!");
return null;
}
addNode(num);
JosephRingProblem.list();
JRPNode curr=first;
/*得到游戏开始的节点*/
while (curr.getNo() != startIndex) {
curr = curr.getNext();
}
/*定义一个数组存储出圈的顺序*/
int[] outQueue=new int[num];
for (int i1 = 0; i1 < num; i1++) {
/*每一次内循环都会得到出圈人之前的那个节点,因为这样更容易删除出圈节点,所以内循环是k-1次*/
for (int i = 0; i < k-1; i++) {
curr=curr.getNext();
}
/*出圈的编号*/
outQueue[i1]= curr.getNext().getNo();
if (i1==num-1){
System.out.println("最后胜利的是编号为"+outQueue[i1]+"的人");
}else {
System.out.println("编号为"+ outQueue[i1]+"的人出圈");
}
/*重新连成环*/
curr.setNext(curr.getNext().getNext());
}
return outQueue;
}
5.打印约瑟夫环
/**
* 方法描述
* @param: []
* @return: void
* @author: zh
* @date: 2021/7/14
* 打印约瑟夫环
*/
public static void list(){
if (first==null){
System.out.println("约瑟夫环为空");
}
JRPNode curr=first;
while (true) {
System.out.println(curr);
if (curr.getNext()==first){
break;
}
curr=curr.getNext();
}
}
6.测试主方法
public static void main(String[] args) {
JosephRingProblem josephRingProblem = new JosephRingProblem();
int[] outQueue = josephRingProblem.outCircle(7,4,7);
System.out.println(Arrays.toString(outQueue));
}
7.完整代码
package LinkedList;
import java.util.Arrays;
/**
* @author zou
* @ClassName : PACKAGE_NAME.约瑟夫环问题
* @Description : 类描述
* Created by user on 2021-07-14 14:35:43
* Copyright 2020 user. All rights reserved.
* 约瑟夫问题:
* 场景:
* 一堆猴子都有编号,编号是1,2,3 ...m,这群猴子(m个)按照1-m的顺序围坐一圈,
* 从第1开始数,每数到第N个,该猴子就要离开此圈,这样依次下来,
* 直到圈中只剩下最后一只猴子,则该猴子为大王。
* 解决方法:单向环形链表或数组(取模,就像环形队列)
*/
public class JosephRingProblem{
/*创建第一个节点*/
private static JRPNode first=null;
/**
* 方法描述
* @param: [num]
* @return: void
* @author: zh
* @date: 2021/7/14
* 创建约瑟夫环(指定节点个数)
*/
public static void addNode(int num){
if (num<1){
System.out.println("无法创建约瑟夫环!");
return;
}
/*记录当前节点*/
JRPNode curr=new JRPNode();
for (int i = 1; i <= num; i++) {
JRPNode node=new JRPNode(i) ;
/*当是第一个节点时,需要自己连自己形成一个环状*/
if (i==1){
/*初始化首节点*/
first=node;
/*因为只有首节点,所以将首节点的next域指向首节点形成环装*/
first.setNext(first);
/*设置当前节点为首节点*/
curr=first;
}else {
/*由于第一次当前的节点指向first节点,即就是从first->first ---->first->node first(即断了环)*/
curr.setNext(node);
/*设置当前节点为新添加的节点*/
curr=node;
/*当前节点最终指向首节点,形成环first->node->first*/
curr.setNext(first);
}
}
}
/**
* 方法描述
* @param: [startIndex, k, num]
* @return: int
* @author: zh
* @date: 2021/7/14
* 约瑟夫环出圈(开始索引,一轮经过节点的个数,约瑟夫环节点的个数)
*/
public int[] outCircle(int startIndex, int k,int num){
if (k<1){
System.out.println("k值有问题!");
return null;
}
addNode(num);
JosephRingProblem.list();
JRPNode curr=first;
/*得到游戏开始的节点*/
while (curr.getNo() != startIndex) {
curr = curr.getNext();
}
/*定义一个数组存储出圈的顺序*/
int[] outQueue=new int[num];
for (int i1 = 0; i1 < num; i1++) {
/*每一次内循环都会得到出圈人之前的那个节点,因为这样更容易删除出圈节点,所以内循环是k-1次*/
for (int i = 0; i < k-1; i++) {
curr=curr.getNext();
}
/*出圈的编号*/
outQueue[i1]= curr.getNext().getNo();
if (i1==num-1){
System.out.println("最后胜利的是编号为"+outQueue[i1]+"的人");
}else {
System.out.println("编号为"+ outQueue[i1]+"的人出圈");
}
/*重新连成环*/
curr.setNext(curr.getNext().getNext());
}
return outQueue;
}
/**
* 方法描述
* @param: []
* @return: void
* @author: zh
* @date: 2021/7/14
* 打印约瑟夫环
*/
public static void list(){
if (first==null){
System.out.println("约瑟夫环为空");
}
JRPNode curr=first;
while (true) {
System.out.println(curr);
if (curr.getNext()==first){
break;
}
curr=curr.getNext();
}
}
public static void main(String[] args) {
JosephRingProblem josephRingProblem = new JosephRingProblem();
int[] outQueue = josephRingProblem.outCircle(7,4,7);
System.out.println(Arrays.toString(outQueue));
}
}
/*约瑟夫环节点*/
class JRPNode{
/*no(索引)*/
private int no;
/*next*/
private JRPNode next;
public JRPNode() {
}
public JRPNode(int no) {
this.no = no;
}
public JRPNode( int no, JRPNode next) {
this.no = no;
this.next = next;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public JRPNode getNext() {
return next;
}
public void setNext(JRPNode next) {
this.next = next;
}
@Override
public String toString() {
return "JRPNode{" +
"no=" + no +
'}';
}
}