约瑟夫环?简简单单!!(java)


约瑟夫问题:
场景:
一堆猴子都有编号,编号是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 +
                '}';
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值