Sliding Window

上周末终于有空参加LeetCode的比赛了,而且这还是我第一次在比赛中完成所有的题目。虽然加上罚时超了比赛时间,但和以前相比也算是很大的进步。这次的题目比较简单,前两道就是暴力解法,第三道是动态规划的简单应用,最后一道比较有意思,我一开始超时了,后来发现可以用滑动窗口的思想优化。滑动窗口即Sliding Window,其实我不确定滑动窗口是不是一个算法,因为我在书上并没有看到过这个算法的介绍,但滑动窗口的思想是非常棒的,在很多题目中都能用到,接下来就以这次的最后一题为例介绍滑动窗口。

滑动窗口

滑动窗口即在数组上维护一个区间,不断调整首尾直到遍历数组。适用滑动窗口的题目往往要求找到数组中的一个区间,如果遍历数组中所有的区间,时间复杂度为O(n2),而滑动窗口的时间复杂度为O(n)。

Smallest Rotation with Highest Score

问题

给出一个数组,可以将这个数组的每一位元素往左移(越界的元素补到最右边)得到一个新的数组,长度为n的数组一共可以得到n个不同的数组。现在给每个数组打分,一个数组每一位元素的值若不大于下标则得1分,求最高分的数组。

思路

一开始我想得很简单,求出每个数组的分数再取最高分的数组,结果超时了。我在求数组分数的时候是遍历数组的每一位元素,对每一位元素对应可以得分的数组加1分,即每一位元素的操作时间复杂度为O(n)。事实上每一位元素对应可以得分的数组是连续的,可以看作区间仅记录首尾,即第一个数组得分比前一个数组多1,最后一个数组得分比后一个数组多1。这样我们就可以得到每一个数组的分数与前一个数组分数的关系,遍历一次就可以得到最高分的数组了。

代码

class Solution {
public:
    int bestRotation(vector<int>& A) {
        vector<int> v(A.size()+1, 0);  //调整长度防止越界
        for (int i = 0; i < A.size(); ++i) {
            if (A[i] <= i) {  //元素不大于下标
                ++v[0];  //从移动0位开始得分
                --v[i + 1 - A[i]];  //左移下标不断减小,直到元素大于下标,结束得分
                ++v[i + 1];  //元素左移越界,补到最右端,下标达到最大,开始得分
            } else {  //元素大于下标
                ++v[i + 1];  //左移下标不断减小直到越界,元素补到最右端,下标达到最大,开始得分
                --v[i + 1 + A.size() - A[i]];  //左移下标不断减小,直到元素大于下标,结束得分
            }
        }
        int K = 0;
        int ans = v[0];  //记录数组得分
        int max = ans;
        for (int i = 1; i < A.size(); ++i) {
            ans += v[i];  //由前一个数组分数得到当前数组分数
            if (ans > max) {
                K = i;
                max = ans;
            }
        }
        return K;
    }
};

感想

这次比赛完我觉得LeetCode的题目还是比较简单的,虽然有时候需要一些巧妙的思路,但总体上不会太复杂,比较适合用来熟悉算法和学习思路。以后我打算找些复杂点的题目来做,比如CodeForce上的题,用来提高编程竞赛的能力。这个学期我想把主要精力放在准备ACM校赛上,看看自己编程的极限在哪里,希望对以后的学习和工作有所帮助。

猜你喜欢

转载自blog.csdn.net/fast_g/article/details/79548956