最近找实习, 在做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 }
这样感觉清爽多了.