目录
[42. 接雨水](#42. 接雨水)
[503.下一个更大元素II - 實作](#503.下一个更大元素II - 實作)
[42. 接雨水 - 實作](#42. 接雨水 - 實作)
[原思路 - 錯誤 (縱向運算)](#原思路 - 錯誤 (縱向運算))
[42. 接雨水](#42. 接雨水)
讀題
503.下一个更大元素II
看完代码随想录之后的想法
一開始看到環形數列的時候有點矇,但是在聽完卡哥的講解後,終於理解了,原來可以使用取mod的做法來模擬環形數列,在影片講解當中,最後有提到一個問題,假設在一個遞減的數列當中,比如說是[4, 3, 2] 那環形則會變成 [4,3,2,4,3,2]對應到的result則會是[-1, 4, ,4, -1, -1] 有提到後面的值會不會覆蓋到前面,我想說的是不會,因為只有當遇到了比較大的值才會觸發更新,那3, 2唯一會碰到也只會是在第二個4才會觸發更新,假設改為[1,4,3,2,1,4,3,2] 也一樣,最終1還是會更新到他的右邊第一個最大的數值。
42. 接雨水
看完代码随想录之后的想法
我原本的思路是縱向運算,但是使用到錯誤的方法,應該是要找左邊最大的以及右邊最大的,我的作法少了這點,看完隨想錄的講解後,才豁然開朗,使用橫向運算比較直覺一點,在理解上也比較清晰,如果遇到右邊比較大的元素,那就是右節點減去當前棧頂的下一個元素,再減去1就會是中間的的寬度,高度則是棧頂的左右元素取最小的減去棧頂對應的数組,兩者相乘,加到result當中就好了,用這個思路去理解就清晰很多了。
503.下一个更大元素II - 實作
思路
- 單調棧存放的是數組下標
- 單調棧性質為遞增
- 將遍歷大小擴為兩倍數組大小,並將i進行取mod運算。
Code
cpp
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
stack<int> st;
vector<int> results(nums.size(), -1);
for(int i = 0; i < (nums.size() * 2) ; i++) {
while(!st.empty() && nums[st.top()] < nums[i % nums.size()]) {
results[st.top()] = nums[i % nums.size()];
st.pop();
}
st.push(i % nums.size());
}
return results;
}
};
42. 接雨水 - 實作
思路
原思路錯誤點
原思路如果改變作法就會對了,因為是做縱向的運算,所以要找到左邊最大以及右邊最大的柱子,如果使用單調棧,則只會找到左邊以及右邊第一個比當前大的元素,而不是最大的,因此會少計算了一些面積。
雙指針縱向運算思路
- 使用兩個數組紀錄左邊最高以及右邊最高
- maxLeft 由左向右遍歷 maxLeft當前高度 max(當前高度,maxLeft前一個高度)
- maxRight 由右向左遍歷 maxRight當前高度 max(當前高度,maxRight前一個高度)
- 求和,遍歷数組,左右最高取最小,並減去當前高度
- 假設大於0,則加入到結果當中
單調棧橫向運算思路
- 單調棧存放元素為數組下標
- 單調棧性質為遞增
- 當前元素對應的数組值與棧頂元素對應的数組值比較與處理
- 小於
- 將當前元素放入棧頂
- 等於
- pop棧頂元素
- 將當前元素放入棧頂
- 因為相等的話,如果比較最小值時,當前元素與棧頂元素相減 = 0 無論如何相乘都只會是0
- 大於
- 儲存棧頂元素
- pop棧頂
- 高度是
- 當前元素與目前棧頂元素對應的数組數值取最小減去之前儲存棧頂元素對應的数組數值
- 寬度是
- 當前元素減去目前棧頂元素再減去1
- sum += h * w
- 儲存當前元素到單調棧中
- 小於
- return sum.
Code
原思路 - 錯誤 (縱向運算)
cpp
class Solution {
public:
int trap(vector<int>& height) {
stack<int> st;
vector<int> water(height.size(), 0);
vector<int> leftH(height.size(), 0);
int water = 0;
for(int i = 0; i < height.size() ; i++) {
while(!st.empty() && height[st.top()] < height[i]) {
rightH[st.top()] = height[i];
st.pop();
}
st.push(i);
}
while (!st.empty()) st.pop();
for(int i = height.size() - 1; i >= 0; i--) {
while(!st.empty() && height[st.top()] < height[i]) {
leftH[st.top()] = height[i];
st.pop();
}
st.push(i);
}
for(int i = 0; i < height.size(); i++) {
if(min(rightH[i], leftH[i]) - height[i] >= 0)water += min(rightH[i], leftH[i]) - height[i];
}
return water;
}
};
雙指針縱向運算思路
cpp
class Solution {
public:
int trap(vector<int>& height) {
vector<int> maxLeft(height.size(), 0);
vector<int> maxRight(height.size(), 0);
int size = height.size();
maxLeft[0] = height[0];
for (int i = 1; i < size; i++) {
maxLeft[i] = max(height[i], maxLeft[i - 1]);
}
maxRight[size - 1] = height[size - 1];
for (int i = size - 2; i >= 0; i--) {
maxRight[i] = max(height[i], maxRight[i + 1]);
}
int sum = 0;
for (int i = 0; i < size; i++) {
int count = min(maxLeft[i], maxRight[i]) - height[i];
if (count > 0) sum += count;
}
return sum;
}
};
單調棧橫向運算思路
cpp
class Solution {
public:
int trap(vector<int>& height) {
stack<int> st;
st.push(0);
int sum = 0;
for(int i = 1; i < height.size() ; i++) {
if(height[st.top()] > height[i]){
st.push(i);
}
else if(height[st.top()] == height[i]){
st.pop();
st.push(i);
}
else
{
while(!st.empty() && height[st.top()] < height[i]) {
int mid = st.top();
st.pop();
if(!st.empty()) {
int h = min(height[i], height[st.top()]) - height[mid];
int w = i - st.top() - 1;
sum += h * w;
}
}
st.push(i);
}
}
return sum;
}
};
總結
自己实现过程中遇到哪些困难
困難點主要是接雨水沒有想到可以這樣利用單調棧的性質,以及自己一開始在做接雨水題目時思路想成縱向運算,看完影片後才比較知道自己為甚麼做錯了。
今日收获,记录一下自己的学习时长
今天大概學習2hr,主要是了解了如何處理環形數列以及單調棧如何在接雨水這個題目中運用
相關資料
● 今日学习的文章链接和视频链接
503.下一个更大元素II
https://programmercarl.com/0503.下一个更大元素II.html