算法: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校賽上,看看自己編程的極限在哪裏,希望對以後的學習和工作有所幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章