42. 接雨水
題目連結:https://leetcode.cn/problems/trapping-rain-water/
文章講解:https://programmercarl.com/0042.接雨水.html
題目難度:困難
影片講解:https://www.bilibili.com/video/BV1uD4y1u75P/
題目狀態:這道題目在LeetCode Top100中做過,使用兩種方法,再回顧一下
思路一:單調棧
-
棧的作用:
- 棧用於儲存柱子的索引,確保棧中的高度是遞減的。
-
遍歷陣列:
- 對於每個柱子,如果當前柱子高度大於棧頂柱子高度,說明可以形成一個凹槽來積水。
-
計算積水:
- 彈出棧頂元素,作為凹槽的底部。
- 如果棧為空,說明沒有左邊界,無法積水。
- 否則,計算左邊界(新的棧頂)與當前柱子之間的寬度。
- 計算高度差:
min(height[left], height[i]) - height[top]
。 - 計算當前積水量並累加到結果中。
-
繼續遍歷:
- 將當前柱子的索引壓入棧中,繼續處理下一個柱子。
程式碼一:
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;
}
};
消耗一:
思路二:動態規劃
-
初始化:
- 檢查輸入陣列是否為空。如果是,直接返回0。
-
計算左邊最大高度:
- 建立一個陣列
leftMax
,其中leftMax[i]
儲存從位置0到位置i的最大高度。 - 透過遍歷陣列,逐步更新
leftMax
。
- 建立一個陣列
-
計算右邊最大高度:
- 建立一個陣列
rightMax
,其中rightMax[i]
儲存從位置i到陣列末尾的最大高度。 - 透過反向遍歷陣列,逐步更新
rightMax
。
- 建立一個陣列
-
計算總積水量:
- 遍歷每個位置,計算當前位置能儲存的水量:
min(leftMax[i], rightMax[i]) - height[i]
。 - 將每個位置的水量累加到總水量中。
- 遍歷每個位置,計算當前位置能儲存的水量:
-
返回結果:
- 返回總積水量。
程式碼二:
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;
}
};
消耗二:
84. 柱狀圖中最大的矩形
題目連結:https://leetcode.cn/problems/largest-rectangle-in-histogram/
文章講解:https://programmercarl.com/0084.柱狀圖中最大的矩形.html
題目難度:困難
影片講解:https://www.bilibili.com/video/BV1Ns4y1o7uB/
題目狀態:不會做,看題解
思路一:雙指標
-
初始化:
minLeftIndex[0]
初始化為-1
,表示最左邊界。minRightIndex[size - 1]
初始化為size
,表示最右邊界。
-
計算
minLeftIndex
:- 從左到右遍歷柱子,對於每個柱子
i
,向左尋找第一個高度小於heights[i]
的柱子。 - 使用變數
t
從i-1
開始向左查詢,更新minLeftIndex[i]
為找到的下標。 - 如果當前柱子
t
的高度大於等於heights[i]
,繼續向左查詢minLeftIndex[t]
。
- 從左到右遍歷柱子,對於每個柱子
-
計算
minRightIndex
:- 從右到左遍歷柱子,對於每個柱子
i
,向右尋找第一個高度小於heights[i]
的柱子。 - 使用變數
t
從i+1
開始向右查詢,更新minRightIndex[i]
為找到的下標。 - 如果當前柱子
t
的高度大於等於heights[i]
,繼續向右查詢minRightIndex[t]
。
- 從右到左遍歷柱子,對於每個柱子
-
計算最大矩形面積:
- 遍歷每個柱子
i
,計算以該柱子為高的最大矩形面積:heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1)
。 - 更新
result
為所有計算出的矩形面積的最大值。
- 遍歷每個柱子
-
返回結果:
- 返回
result
,即最大矩形的面積。
- 返回
程式碼一:
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int len = heights.size();
vector<int> minLeftIndex(len);
vector<int> minRightIndex(len);
minLeftIndex[0] = -1;
for(int i = 1; i < len; ++i) {
int t = i - 1;
while(t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
minLeftIndex[i] = t;
}
minRightIndex[len - 1] = len;
for(int i = len - 2; i >= 0; --i) {
int t = i + 1;
while(t < len && heights[t] >= heights[i]) t = minRightIndex[t];
minRightIndex[i] = t;
}
int ans = 0;
for(int i = 0; i < len; ++i) {
int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
ans = max(sum, ans);
}
return ans;
}
};
消耗一:
思路二:單調棧
-
初始化棧:
- 插入
0
後,棧中初始含有下標0
。
- 插入
-
遍歷柱子:
- 從下標
1
開始遍歷heights
陣列。
- 從下標
-
處理情況:
- 當
heights[i] < heights[st.top()]
時,表示可以計算以棧頂柱子為高的矩形面積。 - 不斷彈出棧頂元素,直到棧為空或棧頂柱子高度不大於當前柱子高度。
- 計算面積:
mid
是當前彈出的柱子下標。w = i - st.top() - 1
是矩形的寬度。h = heights[mid]
是矩形的高度。- 更新
result
為最大值。
- 當
-
返回結果:
- 返回
result
,即最大矩形的面積。
- 返回
程式碼二:
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int ans = 0;
stack<int> st;
heights.insert(heights.begin(), 0);
heights.push_back(0);
st.push(0);
for(int i = 1; i < heights.size(); ++i) {
if(heights[i] >= heights[st.top()]) st.push(i);
else {
while(!st.empty() && heights[i] < heights[st.top()]) {
int mid = st.top();
st.pop();
if(!st.empty()) {
int left = st.top();
int right = i;
int w = right - left - 1;
int h = heights[mid];
ans = max(ans, w * h);
}
}
st.push(i);
}
}
return ans;
}
};
消耗二: