「LeetCode Top100」之雙指標

云雀AC了一整天發表於2024-08-10

283. 移動零

題目連結:https://leetcode.cn/problems/move-zeroes/description/?envType=study-plan-v2&envId=top-100-liked
題目難度:簡單
標籤:陣列雙指標
題目狀態:AC

思路:

兩個指標,i 用來找 0,j 用來找 非0。當nums[i] == 0 && nums[j] != 0時,將兩者交換。

程式碼:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int i = 0;
        for(int j = 1; j < nums.size(); ++j) {
            if(nums[i] == 0 && nums[j] != 0) {
                swap(nums[i], nums[j]);
            }
            if(nums[i] != 0) i++;
        }
    }
};

11. 盛最多水的容器

題目連結:https://leetcode.cn/problems/container-with-most-water/description/?envType=study-plan-v2&envId=top-100-liked
題目難度:中等
標籤:貪心陣列雙指標
題目狀態:ChatGPT協助

思路:

前後指標,依次遍歷,直到前後指標交叉了,每次遍歷的時候記錄當前遍歷得到的最大容量。

程式碼:

class Solution {
public:
    int maxArea(vector<int>& height) {
        int max = 0;
        int left = 0;
        int right = height.size() - 1;
        while(left < right) {
            if(height[left] < height[right]) {
                max = std::max(max, (right - left) * height[left]);
                left++;
            } else {
                max = std::max(max, (right - left) * height[right]);
                right--;
            }
        }
        return max;
    }
};

15. 三數之和

題目連結:https://leetcode.cn/problems/3sum/description/?envType=study-plan-v2&envId=top-100-liked
題目難度:中等
標籤:陣列雙指標排序
題目狀態:AC

思路:

首先對陣列進行排序,然後用一個指標來固定第一個元素,之後使用雙指標來遍歷陣列後面的其他元素,尋找三數之和為目標值的三個數,具體細節看程式碼。

程式碼:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size(); ++i) {
            if(nums[i] > 0 ) break;
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            int left = i + 1;
            int right = nums.size() - 1;
            while(left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if(sum > 0) right--;
                else if(sum < 0) left++;
                else {
                    res.push_back({nums[i], nums[left], nums[right]});
                    while(right > left && nums[right] == nums[right - 1]) right--;
                    while(right > left && nums[left] == nums[left + 1]) left++; 
                    left++;
                    right--;
                } 
            }
        }
        return res;
    }
};

42. 接雨水

題目連結:https://leetcode.cn/problems/trapping-rain-water/description/?envType=study-plan-v2&envId=top-100-liked
題目難度:困難
標籤:陣列雙指標動態規劃單調棧
題目狀態:看題解

題解中給了三種思路,一種動態規劃、一種單調棧,一種雙指標,個人認為動態規劃的方式更好理解,其次是雙指標方法,最難是單調棧。

思路一:動態規劃

這道題記錄了兩個動規陣列leftMax[i]、rightMax[i],leftMax[i]表示當前位置i的左邊最大的值是多少,rightMax[i]表示當前位置i的右邊最大的值是多少。當全部記錄下來之後,再次遍歷陣列,判斷位置i記錄的leftMax和rightMax的最小值,將這個最小值減去i的值,這個值就是積攢的雨水,下圖(來自LeetCode官方題解)更好理解:

image

程式碼一:

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        if (n == 0) {
            return 0;
        }
        vector<int> leftMax(n);
        leftMax[0] = height[0];
        for (int i = 1; i < n; ++i) {
            leftMax[i] = max(leftMax[i - 1], height[i]);
        }

        vector<int> rightMax(n);
        rightMax[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            rightMax[i] = max(rightMax[i + 1], height[i]);
        }

        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans += min(leftMax[i], rightMax[i]) - height[i];
        }
        return ans;
    }
};

思路二:單調棧

  1. 初始化變數:
    • ans:用於儲存總的雨水量。
    • stk:一個棧,用於儲存柱子的索引。
    • n:高度陣列的大小。
  2. 遍歷高度陣列:
    • 對於每個柱子高度height[i]:
      • 檢查棧頂元素:如果當前高度大於棧頂的高度,說明可以形成一個“窪地”。
      • 計算雨水量:
        • 彈出棧頂元素,作為當前窪地的底部。
        • 如果棧為空,說明沒有左邊界,無法形成窪地,繼續下一個。
        • 否則,計算當前窪地的寬度 currWidth 和高度 currHeight:
        • currWidth 是當前索引 i 與新的棧頂 left 之間的距離減一。
        • currHeight 是形成窪地的左右邊界的最小高度減去底部高度。
      • 將窪地的雨水量加到 ans 中。
  3. 將當前索引壓入棧:
    • 繼續處理下一個柱子。
  4. 返回結果:
    • 最終返回累計的雨水量 ans。

程式碼二:

class Solution {
public:
    int trap(vector<int>& height) {
        int ans = 0;
        stack<int> stk;
        int n = height.size();
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && height[i] > height[stk.top()]) {
                int top = stk.top();
                stk.pop();
                if (stk.empty()) {
                    break;
                }
                int left = stk.top();
                int currWidth = i - left - 1;
                int currHeight = min(height[left], height[i]) - height[top];
                ans += currWidth * currHeight;
            }
            stk.push(i);
        }
        return ans;
    }
};

思路三:雙指標

  1. 初始化變數:
    • ans:用於儲存總的雨水量。
    • left 和 right:分別指向陣列的左右兩端。
    • leftMax 和 rightMax:分別記錄從左到右和從右到左的最大高度。
  2. 雙指標遍歷:
    • 當 left 小於 right 時,進行以下操作:
      • 更新 leftMax 和 rightMax,分別為當前指標位置的最大高度。
      • 比較 height[left] 和 height[right]:
      • 如果 height[left] 小於 height[right]:
        • 計算左邊可以儲水的量 leftMax - height[left],並加到 ans。
        • 移動左指標 left 向右。
      • 否則:
        • 計算右邊可以儲水的量 rightMax - height[right],並加到 ans。
        • 移動右指標 right 向左。
  3. 返回結果:
    • 最終返回累計的雨水量 ans。
class Solution {
public:
    int trap(vector<int>& height) {
        int ans = 0;
        int left = 0, right = height.size() - 1;
        int leftMax = 0, rightMax = 0;
        while (left < right) {
            leftMax = max(leftMax, height[left]);
            rightMax = max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                ++left;
            } else {
                ans += rightMax - height[right];
                --right;
            }
        }
        return ans;
    }
};

相關文章