剑指Offer第二十七题:约瑟夫环问题
题目描述
n个学生(学生由0-n-1编号)围成一个圈,老师随机说出一个数字m,学生从第0号位依次开始从0报数,当报数报到m-1时,对应标号的学生出列,并且不再回去;下一个学生又开始从0报数,依次循环,求最后留下来的学生编号。
算法分析与实现
思路一:使用队列,最开始让 0、1、2、3..............n-1,入队列,从0号位开始报数时判断,此时报数的数等不等于m-1,如果等于,则删除标号,出队列;如果不等于则将编号出队列,在重新入队列,到队尾。直到队列的容量为1。
但是我的算法提交牛客网编译器的时候出错,表示这个算法时间复杂度过大。
//时间复杂度巨大。 public static int LastRemaining_Solution(int n, int m) { LinkedList<Integer> queue=new LinkedList<>(); //进队列操作. for(int i=0;i<n;i++){ //集体入栈 queue.add(i); } while (queue.size()!=1){ int i; for(i=0;i<m;i++){ if(i==m-1){ queue.removeFirst(); }else{ int value=queue.getFirst(); queue.removeFirst(); queue.addLast(value); } } } return queue.getFirst(); }
思路二:使用一个数组模拟游戏过程。使用3个标记位,number:表示当前的报数的数字;i:遍历数组的序号;count:表示数组还未被删除的数字。当报数的数字等于给定的数字时,我们将数组赋值为-1,表示已经删除。当i大于数组长度时,i=0重置;如果array[i]==-1,那么表示这个位已经被删除,continue。如果number=m,那么表示这个位置的数该删除了。
public static int findLastNumber(int n,int m){ if(n<1||m<1) return -1; int array[]= new int[n]; int i=-1,number=0,count=n; while(count>0){ i++; if(i>=n) i=0; if(array[i]==-1) continue; number++; if(number==m){ array[i]=-1; number=0; count--; } } return i; }
思路三:采用约瑟夫环的解题策略。
public static int findLastNumber2(int n,int m){ if(n<1||m<1) return -1; ArrayList<Integer> list=new ArrayList<>(); for(int i=0;i<n;i++) list.add(i); int index=-1; while (list.size()!=1){ index=(index+m)%list.size(); list.remove(index); index--; } return list.get(0); }