LeetCode C++ 239. Sliding Window Maximum【Monotonic Queue】困難

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.

Follow up: Could you solve it in linear time?

Example:

Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7] 
Explanation: 

Window position                Max
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

Constraints:

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 1 <= k <= nums.length

題意:給出一個固定大小的區間,區間從數組頭移動到數組尾,返回區間移動過程中的所有區間內部最大值組成的向量。

思路1:暴力做法,超出時間限制。

代碼1:O(n2)\text{O(n}^2\text{)} 的時間複雜度。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ans;
        if (nums.empty()) return ans;
        for (int i = 0; i <= nums.size() - k; ++i) {
            int maxVal = nums[i];
            for (int j = i + 1; j < i + k; ++j) {
                if (nums[j] > maxVal) maxVal = nums[j];
            }
            ans.push_back(maxVal);
        }
        return ans;
    }
};

每次都要和區間中的前 k-1 個數比較,時間浪費巨大。如果高效排除不可能成爲答案的數字,就能夠提高效率。

思路2:改進方法一是:使用優先隊列+記錄數字下標的數組,每次將數字進隊,然後取優先隊列的隊首元素。這將時間提升到 O(nlogn)\text{O(nlogn)} ,能夠通過。

代碼2:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ans;
        priority_queue<int, vector<int>, less<int> > q;
        map<int, int> mp;
        for (int i = 0; i < nums.size(); ++i) {
            q.push(nums[i]);
            //記錄元素的下標, 如果該元素重複出現, 以最大的下標爲準
            mp[nums[i]] = i; 
            while (mp[q.top()] + k <= i) q.pop();
            if (i >= k - 1) ans.push_back(q.top()); 
        } 
        return ans;
    }
};

思路3:使用單調隊列,將普通隊列和優先隊列結合起來,遵循:先進和優先級越高越先出隊的原則。可以達到 O(n)\text{O(n)} 的時間要求。

代碼3:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ans;
        int *st, *idx, front = 0, rear = -1;
        st = new int[nums.size()], idx = new int[nums.size()];
        for (int i = 0; i < nums.size(); ++i) {
            while (front <= rear && nums[i] > st[rear]) --rear;
            st[++rear] = nums[i];
            idx[rear] = i; //記錄位於rear的元素的原數組下標
            while (idx[front] + k <= i) ++front; //清除超出範圍的首元素
            if (i >= k - 1) ans.push_back(st[front]);
        }
        return ans;
    }
};

另外,這裏可以改成使用 deque 雙端隊列。

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