题目描述

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
 
如果没有小朋友,请返回-1
 
链表C++实现
/*struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    
    int LastRemaining_Solution(int n, int m)
    {
        if(n==0) return -1;
        ListNode *p = NULL;
        ListNode *head = NULL;
        //建立初始链表
        for(int i=0;i<n;i++){
            ListNode *s = new ListNode(i);
            if(p == NULL){//头结点单独处理并保存
                p = s;
                head = p;
            }
            else{
                p->next = s;
                p = s;
            }
        }
        p->next = head;//环形链表
        //退圈过程
        int count = 0;//从0开始计数
        ListNode *q = NULL;//暂存要释放的结点
        while(!(p->next == p)){//当链表中结点数目大于1时执行
            while(count<m-1){
                count++;
                p = p->next;
            }
            q = p->next;//暂存要释放的结点,注意顺序
            p->next = p->next->next;
            count = 0;//重新开始计数
            free(q);
        }
        return p->val;
    }
};

 

java数组实现:

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n == 0) return -1;
        int[] children = new int[n];
        int indexN = 0;//记录下标,
        int count = 0;//计数
        int arrayNum = n;//记录还有几个孩子没有出圈
        while(arrayNum > 0){ //当存在未出圈的孩子时
            while(count<m-1){  //计数
                //只有当children[indexN]==0才代表未出圈,==-1代表已出圈
                if(children[indexN] != -1){  
                    count++;
                }
                indexN = (indexN + 1) % n; //注意取模
            }
            /*
            这里是采坑的地方,一定要注意,
            当count=m-1时,children[indexN]可能已出圈,要循环找到未出圈的那个下标*/
            while (children[indexN] != 0){  
                indexN = (indexN + 1) % n;
            }
            children[indexN] = -1;//找到后出圈
            
            /*开始下一轮的计数*/
            count = 0;
            indexN = (indexN + 1) % n;
            arrayNum--; //圈内人数-1
        }
        if(indexN == 0)
            return n-1;
        else
            return indexN-1;
    }
}

 第二次刷该题:

第二次使用leedcode刷题,如果使用上述模拟的方法,会导致超时

方法一:仍然是模拟,只不过每次我们可以确定下一个要删除的位置:(idx+m1)(modn)  n为当前圈中的数字个数

ArrayList 的 remove 操作在后续移位的时候,其实是内存连续空间的拷贝的!所以相比于LinkedList大量非连续性地址访问,ArrayList的性能是很 OK 的!

class Solution {
    public int lastRemaining(int n, int m) {
        List<Integer> list = new ArrayList<>(n);
        for(int i=0;i<n;i++)
            list.add(i);
        int idx = 0;
        while(n>1){
            idx = (idx + m - 1)%n;
            list.remove(idx);
            n--;
        }
        return list.get(0);
    }
}

方法二:数学解法

//(index + m) % n(当前的个数)
class Solution {
public:
    int lastRemaining(int n, int m) {
        int res = 0;
        // i表示当前的个数, 从2开始倒推
        for (int i = 2; i <= n; ++i)
        {
            res = (res + m) % i;
        }
        return res;
    }
};

 

 

 

posted on 2020-10-26 21:30  曹婷婷  阅读(73)  评论(0编辑  收藏  举报