剑指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);
    }

 

 

 

 

 

posted @ 2018-12-14 16:21  轻抚丶两袖风尘  阅读(197)  评论(0编辑  收藏  举报