有1到100共100个数, 从1开始, 每隔1, 2, 3... 个数拿走一个数, 最后剩下几?(约瑟夫环)

最近找实习, 在做Test Assignment时遇到了这么道题, 就顺便记录下来:
说, 有1到100共100个数, 摆成一个圈. 从1开始, 每隔1, 2, 3, 4 … 个数拿走一个数, 一直循环, 最后剩下几? 具体的讲就是一开始(隔0个数)把 1 拿走, 隔1个数(2)把3拿走, 再隔2个数(4, 5)把6拿走, 再隔3个数(7, 8, 9)把10拿走. 第一圈数到100之后接着从2开始数, 直到最后剩下1个数为止, 请问最后剩下几? 如果是1到n呢?

 1     public static int selectNumber(int n) {
 2         
 3         int result = -1;
 4         ArrayList<Integer> nums = new ArrayList<Integer>();
 5         int index = 0;     // this variable is used to remove numbers from list 
 6         int count = 0;     // count is used to count which numbers should be remove
 7         int pIndex = 0;  // this is used to record previous index
 8         
 9         // generate a list contains numbers from 1 to n
10         for(int i = 1; i <= n; i++) {
11             nums.add(i);
12         }
13         
14         while(nums.size() > 1) {
15             
16             while(index < nums.size()) {
17                 nums.remove(index);     
18                 count++;
19                 pIndex = index;
20                 index += count;
21             }
22             
23             index = count - (nums.size() - pIndex);
24             
25             while(index > nums.size() - 1) {
26                 index = index - nums.size();
27             }
28         }
29         
30         result = nums.get(0);
31         return result;
32     }
33     
34     public static void main(String[] args) {
35         int surviver = selectNumber(100);
36         System.out.println("The surviver is: " + surviver);
37     }

以上就是我的解决方案, 最后留下的数是31

2016.06.04更新:

在我发布这篇博文之后热心网友指出这叫”约瑟夫环”. 于是我就去网上搜了一下, 并找到了一个比较简洁的约瑟夫环代码(这个代码不是我原创, 是在别人代码的基础上修改得来):

 1     /**
 2      * 约瑟夫环:
 3      * n个数字摆成一个环, 从1开始数, 数到m或m的倍数的数字被删除, 然后继续, 直到剩下1个数字为止
 4      */
 5     public static int josephProbBasic(int n, int m) {
 6         ArrayList<Integer> list = new ArrayList<>();
 7         for(int i = 1; i <= n; i++) {
 8             list.add(i);
 9         }
10         // 用于计数
11         int count = 1;
12         // 当list中只剩下1个数时, 结束循环
13         for(int i = 0; list.size() > 1; i++) {
14             // 如果数到list的结尾, 从0开始重新数
15             if(i == list.size()) {
16                 i = 0;
17             }
18             // 在m的倍数时, 删除对应的数
19             if(count % m == 0) {
20                 // 由于删除之后, list中被删除元素之后的元素都会依次向前移动一位, 因此也要把i向前移动一位
21                 list.remove(i--);
22             }
23             count++;
24         }
25         return list.get(0);
26     }

但是这只是普通的约瑟夫环, 而我的问题要更麻烦一点. 于是乎, 我根据上面的思路重写了这个问题的代码:

 1     /**
 2      * 升级版约瑟夫环:
 3      * n个数字摆成一个环, 从1开始数, 每隔m个数字删除一个数字. m的初始值为0, 然后m++, 就是0, 1, 2, 3....
 4      *  这样不断删除, 直到剩下1个数字为止.
 5      * @param n
 6      * @return
 7      */
 8     public static int josephProbV2(int n) {
 9         ArrayList<Integer> list = new ArrayList<>();
10         for(int i = 1; i <= n; i++) {
11             list.add(i);
12         }
13         int count = 0;
14         int m = 0;
15         for(int i = 0; list.size() > 1; i++) {
16             if(i == list.size()) {
17                 i = 0;
18             }
19             // 如果count数到m个数, 就删除对应的数字, 同时count从零开始重新数, m = m + 1
20             if(count == m) {
21                 list.remove(i--);
22                 count = 0;
23                 m++;
24             }else {
25                 count++;
26             }
27         }
28         return list.get(0);
29     }

这样感觉清爽多了. 

 

    原文作者:约瑟夫环问题
    原文地址: https://www.cnblogs.com/zhenyu-go/p/5529618.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞