第三組

兔斯稽發表於2020-11-06

1. LeetCode 31. 下一個排列

大概思路:

  1. 由於是要找到下一個排列,所以變動範圍盡肯能靠後(增加的幅度更小),從陣列末尾開始的第一個降序(倒著看)排列的數到陣列末尾就是陣列的變動範圍,記為[l, r]。
  2. 將區間[l, r]內的數分為區間左端點和剩下的所有數兩個部分,第二部分是一個降序排列的陣列,第一部分即區間左端點的數小於第二部分第一個數。
  3. 要想找到區間[l, r]的下一個排列,由於第二部分已經是最大排列,所以只能改變第一部分,在第二部分找出一個數大於第一部分且最小,將其與第一部分交換位置,然後將第二部分從小到大排列,即可得到下一個排列。
  4. 字尾部分的下一個排列也可以得出整個陣列的下一個排列(遞迴?),若從後面找沒有降序排列數,則將整個陣列從小到大排列。
class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int k = nums.size() - 1;
        while(k > 0 && nums[k] <= nums[k - 1])k--;
        if(k <= 0)reverse(nums.begin(), nums.end());
        else
        {
            int t = k;
            while(t < nums.size() && nums[t] > nums[k - 1])t++;
            swap(nums[k - 1], nums[t - 1]);
            sort(nums.begin() + k, nums.end());
        }
    }
};

2. LeetCode 32. 最長有效括號

大概思路:

  1. 對於任意一個括號陣列字首,若字首中右括號數量大於左括號數量,則該字首應該被捨棄。
  2. 滿足第1點,並且左括號數量等於右括號數量的括號陣列是有效的。
  3. 用雙指標維護一個區間(左指標指向區間前一個數),維護區間左右括號數量,若左括號數等於右括號數,則更新答案;若左括號數小於右括號數,則該區間被捨棄,左指標指向右指標;若左括號數大於右括號數,則需要找到與該右括號數相匹配左括號左邊第一個左括號的位置來更新答案(最麻煩的一種情況)
  4. 可以利用棧來實現,棧中存放雙指標指向區間的所有左括號的位置,若右指標指向元素為左括號,則將該位置入棧;若右指標指向元素為右括號,此時若棧為空(對應第二種情況),若棧中只有一個元素(第一種情況),若棧中有多於1個元素(第三種情況)。
class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int>a;
        int res = 0;
        for(int l = -1, r = 0; r < s.size(); r++)
        {
            if(s[r] == '(')a.push(r);
            else
            {
                if(a.size())
                {
                    a.pop();
                    if(a.size())res = max(res, r - a.top());
                    else res = max(res, r - l);
                }
                else l = r;
            }
        }
        return res;
    }
};

3. LeetCode 40. 組合總和 II

大概思路:

  1. 與組合總和I類似,多了一個數量限制(將整個陣列排序,若有n個相同數(記為x)相鄰,則x的數量限制為n。
  2. dfs,具體處理過程如下
class Solution {
public:
    vector<vector<int>>ans;
    vector<int>path;
    vector<vector<int>> combinationSum2(vector<int>& c, int target) {
        sort(c.begin(), c.end());
        dfs(c, 0, target);
        return ans;
    }
    void dfs(vector<int>c, int u, int target)
    {
        if(target == 0)
        {
            ans.push_back(path);
            return;
        }

        if(u == c.size())return;

        int k = u + 1;
        while(k < c.size() && c[k] == c[u])k++;
        int cnt = k - u;

        for(int i = 0; i * c[u] <= target && i <= cnt; i++)
        {
            dfs(c, k, target - i * c[u]);
            path.push_back(c[u]);
        }

        for(int i = 0; i * c[u] <= target && i <= cnt; i++)
            path.pop_back();
    }
};

4. LeetCode 41. 缺失的第一個正數

大概思路:

  1. 桶排序,將所有正數放入正數-x(x=1)的下標的位置。
  2. 從前往後掃描一遍,找到第一個a[nums[i]-1]不等於nums[i[的數
  3. 由於是正數(從1開始)所以x=1,可以擴充到x=其他的情形。
class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        int res = 0;
        for(int i = 0; i < n; i++)
        {
            while(nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] != nums[i])
                swap(nums[i], nums[nums[i] - 1]);
        }
        for(int i = 0; i < n; i++)
            if(nums[i] != i + 1)
            {
                res = i + 1;
                break;
            }
        if(!res)res = n + 1;
        return res;
    }
};

5. LeetCode 42. 接雨水

大概思路1:

  1. 統計每根柱子上面能有多少雨水,求所有柱子之和即可。
  2. 每根柱子上方最多能接多少雨水取決於其左邊最高的柱子和右邊最高的柱子,取兩者的較小值-該柱子高度。若結果小於等於0說明該柱子上方沒有雨水。
class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size(), res = 0;
        if(n == 0)return 0;
        vector<int>left_max(n), right_max(n);
        left_max[0] = height[0], right_max[n - 1] = height[n - 1];
        for(int i = 1; i < n; i++)
            left_max[i] = max(left_max[i - 1], height[i]);
        for(int i = n - 2; i >= 0; i--)
            right_max[i] = max(right_max[i + 1], height[i]);
        for(int i = 1; i < n - 1; i++)
            res += min(left_max[i], right_max[i]) - height[i];
        return res;
    }
};

大概思路2:

  1. 更通用也稍微麻煩一點,利用一個單調遞減棧(高度相同只保留最右邊的那個)
  2. 只要新訪問的元素大於等於棧頂,就一邊不斷彈出棧頂,一邊更新答案(次棧頂<=h[i]:答案加上次棧頂與棧頂之差(即h)與i到次棧頂距離差(即w)的積;次棧頂>h[i]:答案加上h[i]與棧頂之差(即h)與i到次棧頂距離差(即w)的積)
  3. 程式碼中棧頂用last表示,次棧頂用stk,top()表示,棧中存放下標(計算w要用到)。
class Solution {
public:
    int trap(vector<int>& h) {
        stack<int>stk;
        int n = h.size(), res = 0;
        for(int i = 0; i < n; i++)
        {
            int last = 0;
            while(stk.size() && h[stk.top()] <= h[i])
            {
                res += (h[stk.top()] - last) * (i - stk.top() - 1);
                last = h[stk.top()];
                stk.pop();
            }
            if(stk.size())res += (h[i] - last) * (i - stk.top() - 1);
            stk.push(i);
        }
        return res;
    }
};

相關文章