Leetcode滑動窗口題筆記

記錄一下在leetcode上刷的滑動窗口的幾道題,方便後邊複習。
解析都放在代碼裏註釋。

leetcode 15:

/*
給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重複的三元組。

注意:答案中不可以包含重複的三元組。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/3sum


*/

/*
用排序加雙指針,這個題主要是去重
*/
#include<iostream>
#include<vector>
#include<algorithm>
using std::cout;
using std::cin;
using std::vector;
using std::endl;
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        auto n = nums.size();
        vector<vector<int>> res;
        if (n < 3)
        {
            return {};
        }
        
        std::sort(nums.begin(),nums.end());  //先排序

        for (int i = 0; i < n - 2; i++)
        {
            if (nums[i] > 0)
            {
                break;
            }
            if (i > 0 && nums[i - 1] == nums[i])
            {
                continue;
            }
            int L = i + 1;          //兩個指針一個指左邊一個右邊
            int R = n - 1;
            while (L < R)
            {
                if (nums[i] + nums[L] + nums[R] == 0)
                {
                    res.push_back({ nums[i],nums[L],nums[R] });
                    while (nums[L + 1] == nums[L] && L < R)
                    {
                        L = L + 1;
                    }
                    while (nums[R - 1] == nums[R] && L < R)
                    {
                        R = R - 1;
                    }
                    L = L + 1;
                    R = R - 1;
                }
                else if(nums[i] + nums[L] + nums[R] > 0)  //大於0的話就要減少 讓右邊的減小
                {
                    R = R - 1;
                }
                else
                {
                    L = L + 1;
                }

                
            }

        }
        return res;


    }
};

leetcode 1423:

/*
幾張卡牌 排成一行,每張卡牌都有一個對應的點數。點數由整數數組 cardPoints 給出。

每次行動,你可以從行的開頭或者末尾拿一張卡牌,最終你必須正好拿 k 張卡牌。

你的點數就是你拿到手中的所有卡牌的點數之和。

給你一個整數數組 cardPoints 和整數 k,請你返回可以獲得的最大點數。

 

示例 1:

輸入:cardPoints = [1,2,3,4,5,6,1], k = 3
輸出:12
解釋:第一次行動,不管拿哪張牌,你的點數總是 1 。但是,先拿最右邊的卡牌將會最大化你的可獲得點數。最優策略是拿右邊的三張牌,最終點數爲 1 + 6 + 5 = 12 。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
*/

#include<iostream>
#include<vector>

using namespace std;

class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        int n = cardPoints.size();
        int max = 0;
        //如果k要是大於等於n的話 就全加起來
        if (k >= n)
        {
            for (int i = 0; i < n; i++)
            {
                max = max + cardPoints[i];
            }
            return max;
        }
        int ans;
        int sum=0;
        vector<int> vec(k,0);
        //初始化滑動窗口
        for (int i = 0; i < k; i++)
        {
            vec[i] = cardPoints[i];
            sum = sum + cardPoints[i];
        }
        ans = sum;
    
        //依次滑動
        for (int i = 1; i <= k; i++)
        {
            sum = sum + (cardPoints[n - i] -vec[k-i]);
            if (sum > ans)
            {
                ans = sum;
            }
  
        }
        return ans;

    }
};

leetcode 1052:

/*
今天,書店老闆有一家店打算試營業 customers.length 分鐘。每分鐘都有一些顧客(customers[i])會進入書店,所有這些顧客都會在那一分鐘結束後離開。

在某些時候,書店老闆會生氣。 如果書店老闆在第 i 分鐘生氣,那麼 grumpy[i] = 1,否則 grumpy[i] = 0。 當書店老闆生氣時,那一分鐘的顧客就會不滿意,不生氣則他們是滿意的。

書店老闆知道一個祕密技巧,能抑制自己的情緒,可以讓自己連續 X 分鐘不生氣,但卻只能使用一次。

請你返回這一天營業下來,最多有多少客戶能夠感到滿意的數量。
 

示例:

輸入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], X = 3
輸出:16
解釋:
書店老闆在最後 3 分鐘保持冷靜。
感到滿意的最大客戶數量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.

*/
#include<iostream>
#include<vector>
#include<deque>
using namespace std;

class Solution {
public:
    int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int X) {
        int n = customers.size();
        int i;
        int ans=0;
        for (i = 0; i < n; i++)
        {
            if (grumpy[i] == 0)
            {
                ans = ans + customers[i];
            }
        }
        //發動技能的時候滑動窗口,窗口裏面放下標
        
        deque<int> vec(X,0);
        for (i = 0; i < X; i++)
        {
            vec[i] =i;
            if (grumpy[i] == 1)
            {
                ans = ans + customers[i];
            }
        }
        int max = ans;
        for (int k = 1; k < n - X + 1; k++)
        {
            if (grumpy[vec[0]] == 1)
            {
                max = max - customers[vec[0]];
            }
            vec.pop_front();
            vec.push_back(X+k-1);
            if (grumpy[X + k - 1] == 1)
            {
                max = max + customers[X + k - 1];
            }
            if (max > ans)
            {
                ans = max;
           
            }

        }
        return ans;

    }
};

leetcode 295:

/*
中位數是有序列表中間的數。如果列表長度是偶數,中位數則是中間兩個數的平均值。

例如,

[2,3,4] 的中位數是 3

[2,3] 的中位數是 (2 + 3) / 2 = 2.5

設計一個支持以下兩種操作的數據結構:

void addNum(int num) - 從數據流中添加一個整數到數據結構中。
double findMedian() - 返回目前所有元素的中位數。
示例:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
*/
/*
解析:
用兩個堆,最大堆和最小堆,最大堆讓小的數字一半在最大堆,最小堆讓大數字的一半去最小堆。
因爲最大堆是從大到小,最小堆是從小到大,所以呢,中位數就是堆頂。所以要做的就是平衡兩個堆的數目
如果k是奇數 k = 2*n+1 則允許最大堆持有n+1個元素 
當k 是偶數的時候,則兩個堆都是n
*/
#include<iostream>
#include<queue>
#include<vector>
using namespace std;

class MedianFinder {
    priority_queue<int> lo;                              // 最大堆
    priority_queue<int, vector<int>, greater<int>> hi;   // 最小堆

public:
    
    void addNum(int num)
    {
        lo.push(num);                                    

        hi.push(lo.top());                               // 平衡
        lo.pop();

        if (lo.size() < hi.size()) {                     
            lo.push(hi.top());
            hi.pop();
        }
    }

    // 返回中值
    double findMedian()
    {
        return lo.size() > hi.size() ? (double)lo.top() : (lo.top() + hi.top()) * 0.5;
    }
};

leetcode 480:

/*
中位數是有序序列最中間的那個數。如果序列的大小是偶數,則沒有最中間的數;此時中位數是最中間的兩個數的平均數。

例如:

[2,3,4],中位數是 3
[2,3],中位數是 (2 + 3) / 2 = 2.5
給你一個數組 nums,有一個大小爲 k 的窗口從最左端滑動到最右端。窗口中有 k 個數,每次窗口向右移動 1 位。你的任務是找出每次窗口移動後得到的新窗口中元素的中位數,並輸出由它們組成的數組。

 

示例:

給出 nums = [1,3,-1,-3,5,3,6,7],以及 k = 3。

窗口位置                      中位數            //排序的中位數  所以啊 理解錯了

---------------               -----
[1  3  -1] -3  5  3  6  7       1
 1 [3  -1  -3] 5  3  6  7      -1
 1  3 [-1  -3  5] 3  6  7      -1
 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]      6
 因此,返回該滑動窗口的中位數數組 [1,-1,-1,3,5,6]。

*/
/*
在295的基礎上加些東西,也是用兩個堆,但是加一個哈希表,裏面添加那些被移出去的元素
一旦這些被移除去的元素在堆頂,那麼就在堆頂刪除(不是堆頂不好刪除,所以等到在堆頂)
在刪除,因爲在堆裏也不影響找中位數,還是堆頂那個。
但是注意保持平衡的時候,要算有效的數字,那麼被移出窗口但是還沒有從堆裏刪除的元素不能算進去
*/
#include<iostream>
#include<vector>
#include<deque>
#include<queue>
#include<unordered_map>
using namespace std;

class Solution {
public:
    vector<double> medianSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        vector<double> res;
        double ans;
        unordered_map<int, int> hash_table; //哈希表保存被移出窗口的元素
        priority_queue<int> max; //最大堆,裏面保存小的那一半的數字
        priority_queue<int, vector<int>, greater<int>> min; //最小堆,裏面保存大的那一半的元素

        int i = 0;
        int out_num, in_num, balance;

        //初始化兩個堆
        while (i < k)
        {
            max.push(nums[i]);
            i++;
        }
        for (int j = 0; j < k / 2; j++)
        {
            min.push(max.top());
            max.pop();
        }

        while (1)
        {
            if (k % 2 == 0)  //如果k是偶數
            {
                ans = ((double)max.top() + (double)min.top()) / 2;
            }
            else
            {
                ans = max.top();   //因爲我們是允許max裏比min裏多一個的
            }
            res.push_back(ans);
            if (i >= n)   //證明窗口滑完了
            {
                break;
            }
            
            out_num = nums[i-k]; //要被移出去的元素
            in_num = nums[i++];  //要進來的元素
            balance = 0;

            //如果那個要移出去的元素是max裏面的,那麼balance就-1,反之+1
            //因爲我們規定max裏面的元素比min裏面的少 balance<0
            balance += (out_num <= max.top() ? -1 : 1);
            //移出去的元素放進哈希表裏
            hash_table[out_num]++;

            //有出就有進
            if (!max.empty() && in_num <= max.top())  //比max.top()小 那就是進入max裏
            {
                balance++;
                max.push(in_num);
            }
            else
            {
                balance--;
                min.push(in_num);
            }
            //看是不是還平衡
            if (balance < 0)   //max裏面可用的元素少
            {
                max.push(min.top());
                min.pop();
                balance++;
            }
            if (balance > 0)
            {
                min.push(max.top());
                max.pop();
                balance--;
            }

            //如果堆頂元素是被移出去的,要清理
            while (hash_table[max.top()])
            {
                hash_table[max.top()]--;
                max.pop();
            }
            while (!min.empty() && hash_table[min.top()])
            {
                hash_table[min.top()]--;
                min.pop();
            }

        }
        return res;
    
        


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