| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
SylArmenta
9年前发布

约瑟夫问题 java 实现

约瑟夫问题

这是17世纪的法国数学家加斯帕在《数目的游戏问题》中讲的一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,数到第九个人就将他扔入大海。该人后面的人从1开始重新报数,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。

思路
1. 先建立一个类,有 id 和 isRemove

class Person {      int id;      boolean isRemove;        public Person(int id, boolean isRemove) {          this.id = id;          this.isRemove = isRemove;      }        public boolean isRemove() {          return isRemove;      }        public void setRemove(boolean isRemove) {          this.isRemove = isRemove;      }  }
    2.
class Circle {      private ArrayList<Person> circle = new ArrayList<Person>();        private int amount; // 一共多少人        Circle(int amount) {          this.amount = amount;          for (int i = 0; i < amount; i++) {              Person p = new Person(i + 1, false);              circle.add(p);          }      }        /** * * @param index * 最开始扔人的位置,比如第9人 * @param total * 共需要扔几次 */      public void getIndex(int index, int total) {            // 起始位置          int currentIndex = -1;            for (int t = 0; t < total; t++) {              int notRemove = 0;                //本来是notRemove != 9,写死不好              while (notRemove != index) {                  currentIndex++;                  // 或者用 currentIndex % amount 解决                  if (currentIndex == amount) {                      currentIndex = 0;                  }                  if (!circle.get(currentIndex).isRemove) {                      notRemove++;                  }              }                // 将扔的人设为 true              Person p = circle.get(currentIndex);              p.setRemove(true);              circle.set(currentIndex, p);                System.out.printf("第 %-2d 次仍的人id是%4d\n", t + 1, p.id);            }        }    }  

代码建立一个容器来保存各个人,主要就是不移除这个人,而是把他状态设为setRemove(true),好处这样容器的大小保持不变。如果删掉这样人,容器大小每次减1,算起来麻烦

if (currentIndex == amount) { currentIndex = 0; }

本来是用 currentIndex % amount 实现的,不过本代码都是+1,肯定会有 == amount的情况,用置为0更好

结果

第 1  次仍的人id是   9  第 2  次仍的人id是  18  第 3  次仍的人id是  27  第 4  次仍的人id是   6  第 5  次仍的人id是  16  第 6  次仍的人id是  26  第 7  次仍的人id是   7  第 8  次仍的人id是  19  第 9  次仍的人id是  30  第 10 次仍的人id是  12  第 11 次仍的人id是  24  第 12 次仍的人id是   8  第 13 次仍的人id是  22  第 14 次仍的人id是   5  第 15 次仍的人id是  23  

优化

也可以用数组实现,下标和 boolean 正好模拟上面的 Person 类

static void other() {      boolean[] usaJapa = new boolean[30];      // 用类库初始化      Arrays.fill(usaJapa, true);        int leftCount = usaJapa.length;        int countNum = 0;      int index = 0;      int i = 0;      while (leftCount > 15) {          if (usaJapa[index]) {              countNum++;          }            if (countNum == 9) {              countNum = 0;              usaJapa[index] = false;              leftCount--;              System.out.printf("第 %-2d 次仍的人id是%4d\n", ++i, index + 1);            }            index++;          if (index == usaJapa.length) {              index = 0;          }      }    }

用链表实现,remove 其中的数据

class Circle {      private LinkedList<Person> circle = new LinkedList<Person>();        private int amount; // 一共多少人        Circle(int amount) {          this.amount = amount;          for (int i = 0; i < amount; i++) {              Person p = new Person(i + 1, false);              circle.add(p);          }      }        public void othergetIndex(int mark, int total) {          // remain 是剩下的人数,total 是需要删除的人数          int remain = amount;          int count = 0;          int current = 0;          int i = 0;          while (remain > amount - total) {              count++;              // 注意这人如果达到 mark,比如第9个人              // 删掉第9个人后,再删第9个人,就是删原来的第10个人              if (count == mark) {                  // remove 返回的是删除的 Person                  Person p = circle.remove(current);                  System.out.printf("第 %-2d 次仍的人id是%4d\n", ++i, p.id);                  count = 0;                  remain--;              } else {                  current++;              }              if (current == amount - i) {                  current = 0;              }            }        }  }

用 LinkedList 删除操作更快,

Person p = circle.remove(current);
remove 返回删除的元素

LinkedList<Integer> testList = new LinkedList<Integer>();  for (int i = 0; i < 10; i++) {          testList.add(i);  }  for (int i = 0; i < 5; i++) {      System.out.println(testList.remove(3));  }   /** * 3 * 4 * 5 * 6 * 7 */    

这段代码的输出可以看出,删除了第3个元素,后面元素往前移动