黑马程序员技术交流社区

标题: 约瑟夫环 [打印本页]

作者: yzqiong5566    时间: 2012-11-25 00:18
标题: 约瑟夫环
本帖最后由 yzqiong5566 于 2012-11-25 09:12 编辑

著名的约瑟夫环问题详解
问题概述:
  已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
  例如:n = 9, k = 1, m = 5
  【解答】
  出局人的顺序为5, 1, 7, 4, 3, 6, 9, 2, 8。
  下面将通过简单例子来实现约瑟夫环的一个具体问题(笔者尽量把问题简单化):现在有600个人围成一个圈,从编号为0(假设每个人身上都有一个编号,600个人编号从0到599)的那人开始报数,数到3的人便出列,他的下一个人又从1开始报数,数到3的那个人又出列;依次规律重复下去,问:最后一个出列的人原来的编号是多少?
下面用2种解决方法通过eclipse-3.4.2(英文版)编写的代码如下:
方法一:用数组
package wish25MethodNO1;
public class JosephusQuestion {
    public static void main(String[] args) {  
       boolean[] bool = new boolean[600];//定义一个600人的布尔类型的数组用于标示所有人的状态,在圈里的都置为true否则为false
       for(int i = 0;i<bool.length;i++) {
           bool=true;//开始时600人形成一个圈,都在圈里,初始化为true
       }
       int countArrayLength = bool.length;//定义一个整形的变量countArrayLength用来统计在圈里的人数
       int index = 0;//定义一个整形index用来记录在圈里人的编号,即数组的下标
       int count = 0;//定义一个计数器(统计圈里人报的数(1,2,3),当遇到3便重新置为0),用于圈里人的报数
       while(countArrayLength>1) {//当圈里只有一个人的时候会停止循环
           if(bool[index]==true) {//圈里要有人才能开始报数
              count++;//计数器自加1
              if(count == 3) {//如果报到3这个数(就出列)
                  count = 0;//计数器归0
                  bool[index]=false;//报到3的人出列
                  countArrayLength--;//圈里的人数自减1
              }
           }         
           index++;//在圈里的人依次循环报数
           if(index == bool.length) {//当编号为数组长度时(应置为0,否则会报ArrayIndexOutOfBoundsException异常)
              index = 0;//置为0
           }
       }
       System.out.print("最后一个出列的人原来的编号是:");
       for(int i = 0;i<bool.length;i++) {//经过上面的筛除后,留下的便是要找的
           if(bool==true) {
              System.out.println(i);
           }
       }
    }
}



方法二:用面向对象的思想来解决问题
在同一个包里新建了3个类,代码如下:

package wish25MethodNO2;

public class Person {//定义人的类
    public int id;//编号
    public Person leftPerson;//左边的人
    public Person rightPerson;//右边的人
}

package wish25MethodNO2;
public class PersonCircle {//定义人形成的一个圈的类  
    private int number;//用于统计圈里的人数(默认为0)
    private Person firstPerson;//用于标示圈里的第一个人
    private Person lastPerson;//用于标示圈里的最后一个人

    public int getNumber() {//得到圈里的人数
       return number;
    }
    public Person getFirstPerson() {//得到圈里位置为第一的那人
       return firstPerson;
    }


    public PersonCircle(int number) {//定义一生成number个人的圈的构造方法
       for(int i=0; i<number; i++) {
           addPerson();
       }
    }  

    public void addPerson() {//往圈里加人,即形成一个多大的圈
       Person person = new Person();//先new一个人出来以便往圈里加
       person.id = number;//给加进圈里的人上一编号
       if(number<=0) //如果圈里没人时(加进去的那人便充当了所有角色)
       {
           firstPerson = person;
           lastPerson = person;
           person.leftPerson = person;
           person.rightPerson = person;

       } else {//如果有人时   (看下图:1-1)     
           lastPerson.rightPerson = person;//原来最后那人右边的人变成了刚加进来的person这人
           firstPerson.leftPerson = person;//原来第一个人左边的人变成了刚加进来的person这人
           person.leftPerson = lastPerson;//加进来的person这人左边是原来的最后那人
           person.rightPerson = firstPerson;//加进来的person这人右边是原来的第一个那人
           lastPerson = person;//最后,新加进来的person这人变成了圈里的最后一个人
       }
       number++;//圈里人数增加
    }

    public void deletePerson(Person person) {//从圈里删除人
       if(number <= 0) {//如果没人,就退出
           return;
           //System.exit(0);
       } else if(number == 1) {//如果就一个人,删除后都为空
           firstPerson = lastPerson = null;
           person.leftPerson = person.rightPerson = null;
       } else {
           person.leftPerson.rightPerson = person.rightPerson;
           person.rightPerson.leftPerson = person.leftPerson;
           if(person == firstPerson) {//如果删除那人刚好是第一个人
              firstPerson = person.rightPerson;//删除那人person的右边的人往前补上成为新的firstPerson
           } else if(person == lastPerson) {//如果删除那人刚好是最后那个人
              lastPerson = person.leftPerson;//删除那人person的左边的人便补上成为新的lastPerson
           }
       }
       number--;//删除后人数减少
    }
}
package wish25MethodNO2;
public class JosephusQuestionTestDemo {
    public static void main(String[] args) {      
       PersonCircle per = new PersonCircle(600);//生成一个600人的圈
       int count = 0;//定义一个计数器(统计圈里人报的数(1,2,3),当遇到3便重新置为0),用于圈里人的报数
       Person p = per.getFirstPerson();//用圈里的第一个人初始化,开始报数
       while(per.getNumber()>1) {//当圈里人数大于1时循环筛选
           count++;//计数器自加1
           if(count == 3) {//如果报到3这个数(就出列)
              count = 0;//计数器归0
              per.deletePerson(p);//报到3的人被删除
           }
           p = p.rightPerson;//删除人的右边的人重新开始报数
       }
       System.out.println("最后一个出列的人原来的编号是:"+p.id);//最后留下的那人就是要找的
    }
}




作者: 杨千里    时间: 2012-11-25 15:10
哥们 你刚进来么?可以看出你是非常有实力的,有些帖子该加分,有些不该加,请联系你发帖所在的板块版主
我是javaee+云区的小小版主,该板块内我有权利审核任一个帖子,如果你在其它板块发帖或者提问,请联系其他版块 所在的版主

例如:你在 javaee+android区发帖,你认为应该加分而没有给你加,请联系该板块版主,如对加分不满,可以向官方管理员提出异议,谢谢合作
作者: yzqiong5566    时间: 2012-11-25 15:15
杨千里 发表于 2012-11-25 15:10
哥们 你刚进来么?可以看出你是非常有实力的,有些帖子该加分,有些不该加,请联系你发帖所在的板块版主
我 ...

是刚进来的,才完成了基础测试。好的,太谢谢您了。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2