leetcode题解:圆圈中最后剩下的数字

2022/10/08 leetcode 算法 共 1101 字,约 4 分钟

🔗:圆圈中最后剩下的数字

先容我吐槽一下,这是哪门子的简单题,救命!

最直接的思路:模拟!

既然是要删除圆圈中的数字,那么我们可以用环形链表来模拟,虽然说是用环形链表来模拟,我们不需要严格来写,只要保证指针能玩的转,都可以看作环形链表。下面我以deque来实现,实现完了发现不用deque,哪怕用vector都可以实现:

class Solution {
public:
    int lastRemaining(int n, int m) {
        deque<int> circle;
        for(int i=0;i<n;i++)
            circle.push_back(i);
        // i用来记录索引,cnt用来数数
        int cnt=1,i=0;
        while(circle.size()>1){
            if(cnt==m&&i<circle.size()){
                circle.erase(circle.begin()+i);
                //erase以后指针有变化,要--
                i--;
                cnt=0;
            }
            else if(cnt==m){
                i=i%circle.size();
                circle.erase(circle.begin()+i);
                i--;
                cnt=0;
            }
            else{
                cnt++;
                i++;
            }
        }
        return circle[0];
    }
};

这里要非常注意erase的用法。按照STL中的说明,这种erase会改变iterator,除非是在开头和结尾的erase,所以为了不引起混乱,我们要清楚地更新icntcnt很好理解,每次从1数到m就归零。i就需要注意,当每次有erase的操作,i需要回退一位。当i已经过了当前最大的位置时需要取余。需要注意,当circle.begin()所指的位置被erase后,circle.begin()会指向下一个位置。

数学推导

模拟的思路很明确,但是过不了关,会超时。这样我们不得不思考更好的方法。在《剑指Offer》中提供的思路就很好,我附上自己的理解:

IMG_6979

我们对f(n,m)的定义是整个序列,而我们最终需要的是剩下的那一个数,在所有序列中都存在的数字就是我们找的剩下最后一个数字,我们可以把上面总结的规律换成一个递归式: \(f(n,m)=\begin{cases}0&n=1\\\left[f(n-1,m)+m\right]\%n&n>1\end{cases}\) 接下来实现就非常简单了,放一个不用动脑的版本:

class Solution {
public:
    int lastRemaining(int n, int m) {
        if(n==1)
            return 0;
        return (lastRemaining(n-1,m)+m)%n;
    }
};

文档信息

Search

    Table of Contents