问题来历:

约瑟夫问题是个有名的问题:有序号1,2…n个程序员围成一个大圈圈,从编号为K(1<=k<=n)的程序员从1开始报数,数到m的那个程序员出列,可以去挑选对象(真对象,不New不Joke),他的下一位程序员又从1开始报数,数到m的人又出列挑选对象,美滋滋~,依次类推,到最后一个程序员不给对象,只给了一堆bug,并在程序上线的时候祭了天。。。额。。是一个凄美的爱情故事!

思路分析:

构建一个单向环形链表实现:
1.先创建第一个节点,让first指向该节点,并形成环状;
2.后续我们每创建一个新的节点,就把该节点加入到已有的环形链表中;
3.遍历环形链表,先让一个辅助指针cur,指向first节点,然后遍历环形链表,cur.next==first时结束;

出圈顺序分析:
1.需要创建一个辅助指针cur,事先指向环形链表的最后这个节点;
2.程序员报数前,先让first和cur移动k-1次;
3.程序员报数时,让first和cur移动m-1次;
4.这是first指向的节点出圈;

代码实现:

public class Josepfu {
    public static void main(String[] args) {
        CircleLinkedList circleLinkedList= new CircleLinkedList();
        circleLinkedList.isAdd(5);
       // circleLinkedList.isShow();
        circleLinkedList.isCount(1,2,5);
      }
 }
//创建一个环形的单项链表
class CircleLinkedList{
      //创建一个first节点
      private Programmer first=null;
      //添加程序员,构成一个环形链表
      public void isAdd(int nums){
          if(nums<1){
              System.out.println("输入值不正确");
              return ;
          }
          Programmer cur =null;  //辅助指针
          //用for循环创建环形链表 
    for (int i = 1; i <=nums ; i++) {
        Programmer programmer = new Programmer(i);
        if(i==1){  //如果为第一个程序员
            first=programmer;
            first.setNext(first);  //一个节点本身构成一个环
            cur = first;  //辅助指针指向第一个程序员
        }else{
            cur.setNext(programmer);
            programmer.setNext(first);  //形成环状
            cur=programmer; //指针后移
        }
    }
}
//遍历环形链表
public void isShow(){
      //先判断链表是否为空
    if(first==null){
        System.out.println("起锅烧油,请放入程序员~");
        return ;
    }
    //first不能动,需要一个辅助变量
    Programmer cur=first;
    while(true){
        System.out.printf("程序员的编号 %d \n",cur.getNo());
        if(cur.getNext()==first){ //遍历完成
             break;
        }
        cur=cur.getNext();
    }
}
//根据输入,计算程序员出圈顺序
/**
 * @param startNo  从第几个程序员开始数
 * @param countNum   数几下
 * @param nums   最初程序员个数
 */
public void isCount(int startNo,int countNum,int nums){
      if(first==null||startNo<1||startNo>nums){
          System.out.println("参数错误");
          return;
      }
      //创建辅助变量帮助程序员出圈,事先指向环形链表的最后一个节点
      Programmer cur =first;
      while(true){
            if(cur.getNext()==first){  //向环形链表的最后一个节点
                 break;
            }
            cur =cur.getNext();
      }
     //程序员报数前,先让first和cur指针同时移动startNo-1次
    for (int i = 0; i <startNo-1 ; i++) {
          first=first.getNext();
          cur=cur.getNext();
    }
     //程序员报数时,先让first和cur指针同时移动countNum-1次,然后出圈
        while(true){
             if(cur==first){   //圈中只有一个程序员
                    break;
             }
            //让first和cur指针同时移动countNum-1次
            for (int i = 0; i <countNum-1 ; i++) {
                first=first.getNext();
                cur=cur.getNext();
            }
            //first指向的节点,就是要出圈的程序员
            System.out.printf("程序员%d出圈\n",first.getNo());
            first=first.getNext();
            cur.setNext(first);
        }
        System.out.printf("最后留在圈中的程序员%d \n" ,first.getNo());
    }
} 
//创建一个程序员类,表示一个节点
class  Programmer{
      private int no;  //编号
      private Programmer next;  //指向下一个节点,默认null

    public Programmer(int no) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Programmer getNext() {
        return next;
    }

    public void setNext(Programmer next) {
        this.next = next;
    }
}

效果图:

java 约瑟夫序列 约瑟夫环java链表_约瑟夫环