问题来历:
约瑟夫问题是个有名的问题:有序号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;
}
}
效果图: