代码随想录算法训练营DAY56|C++单调栈Part2|503.下一个更大元素II、42.接雨水

503.下一个更大元素II

力扣题目链接

文章链接:503.下一个更大元素II

视频链接:单调栈,成环了可怎么办?LeetCode:503.下一个更大元素II
状态:把环展开应该是最好想的了吧!

数组变成环了,如何处理呢?

思路1:把环展开

我们直接扩充原数组,比如对于数组nums=[1,2,1]扩充成newNums=[1,2,1,1,2,1].

这样对应的结果集就是result=[2,-1,2,2,1,_]我们只关心结果集的前三个元素。

彪叫终点的代码段就是拼接数组和将结果集resize到原大小

代码如下:

class Solution {
public:
  	vector<int> nextGreaterElements(vector<int>& nums) {
      //拼接一个新的nums
      vector<int> nums1(nums.begin(), nums.end());
      nums.insert(nums.end(), nums1.begin(), nums1.end());
      //用新的nums大小来初始化result
      vector<int> result(nums.size(), -1);
      if (nums.size() == 0) return result;
      
      //开始单调栈
      stack<int> st;
      st.push(0);
      for (int i = 1; i < nums.size(); i++) {
        	if (nums[i] < nums[st.top]) st.push(i);
        	else if (nums[i] == nums[st.top()]) st.push(i);
        	else {
          		while (!st.empty() && nums[i] > nums[st.top()]) {
                	result[st.top()] = nums[i];
                	st.pop();
              }
            	st.push(i);
        	}
      }
      //最后把结果集result数组resize到原数组大小
      result.resize(nums.size() / 2);
      return result;
    }
};

思路2: 直接在for循环模拟走两次,然后取余

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> result(nums.size(), -1);
        if (nums.size() == 0) return result;
        stack<int> st;
        st.push(0);
        for (int i = 1; i < nums.size() * 2; i++) { 
            // 模拟遍历两边nums,注意一下都是用i % nums.size()来操作
            if (nums[i % nums.size()] < nums[st.top()]) st.push(i % nums.size());
            else if (nums[i % nums.size()] == nums[st.top()]) st.push(i % nums.size()); 
            else {
                while (!st.empty() && nums[i % nums.size()] > nums[st.top()]) {
                    result[st.top()] = nums[i % nums.size()];
                    st.pop();
                }
                st.push(i % nums.size());
            }
        }
        return result;
    }
};

//精简版代码
class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> result(nums.size(), -1);
        if (nums.size() == 0) return result;
        stack<int> st;
        for (int i = 0; i < nums.size() * 2; i++) {
            // 模拟遍历两边nums,注意一下都是用i % nums.size()来操作
            while (!st.empty() && nums[i % nums.size()] > nums[st.top()]) {
                result[st.top()] = nums[i % nums.size()];
                st.pop();
            }
            st.push(i % nums.size());
        }
        return result;
    }
};

⭐️42.接雨水

力扣题目链接

文章讲解:42.接雨水

视频讲解:单调栈,经典来袭!LeetCode:42.接雨水
状态:相当细节啊,我觉得一定要三种方法全部掌握,非常提升能力。

暴力解法

暴力解法可以按行来计算,也可以按列来计算,按列计算如图所示:

从图中可以看出来我们求每一列的高度就可以计算雨水的量。

并且,每一列雨水的高度,取决于矮的那块"木板",即左侧最高的柱子lHeight和右侧最高的柱子rHeight中较矮的那个柱子的高度。

在这里,雨水面积就等于min(lHeight, rHeight) - height

具体详情可以看卡哥文章:42.接雨水

双指针优化

详细可以看卡哥文章:42.接雨水

单调栈

其实,从暴力解法中,我们已经可以感受到,我们要求该元素右边最大元素和左边最大元素那么我们肯定是可以使用单调栈了。

再强调一遍,使用单调递增栈可以计算出右边第一个最大元素,递减栈可以求出右边第一个最小元素。

我们从上文可以看出,我们需要找到三个元素才能找到该行的面积——当前元素,当前元素左面第一个最大元素,当前元素右边第一个最大元素。

找到三个元素

在前几题的练习中,我们熟悉了单调栈的工作过程,需要当前栈顶元素和当前遍历元素进行一个比较,如果当前遍历元素大于栈顶元素,此时就是我们收获结果的时候,但是这求的是当前元素右边第一个比他大的元素,左边的应该怎么求呢?

其实,左边的第一个最大元素,他已经在栈内了,而且就是栈顶的第二个元素(因为我们栈是单调递增栈,左边第一个最大元素是我们遍历过的,它就在栈内)!

分析好三种情况

这里拿[60,20,20,10,30]举例。

遍历元素小于栈顶元素

首先把60推入栈(⚠️栈内的元素存放的是下标),接下来遍历的是20,20小于60,符合我们单调递增的顺序,所以此时把20直接入栈。[20, 60]

此时,再遍历下一个元素,仍然是20,此时遍历的元素和栈顶元素相同了,其实无论是弹出旧的20还是直接把新20入栈都不影响结果,这里我们选择直接把新20入栈;[20,20,60]

下一个遍历10,直接入栈;[10, 20, 20, 60]

接下来是30,我们发现当前遍历的元素大于栈口元素了,也就是说我们发现凹槽了,那么我们的凹槽元素就是10了,右柱子是30,左柱子是20。

此时开始计算结果:

我们的雨水的高度=min(左边柱子,右边柱子) - 中间柱子高度

雨水的宽度=右边柱子下标-左边柱子下标

mid = st.top();
st.pop();
heightRain = min(h[st.top()], h[i]) - h(mid);
widthRain = i - st.top() - 1;
area = h * w;

继续运行,我们现在已经把10弹出去了,栈顶元素是20,当前元素还是30,应该重复上文流程,那么我们就找一下栈顶元素右边元素还是20。

heightRain = min(20, 30) - 20 = 0

heightRain竟然等于零!说明这接不了雨水,那没办法,我们弹出目前的栈顶元素20继续走上文流程。

现在我们的当前遍历元素是30,栈顶元素是20,栈顶右边第一个元素是60,可以继续走流程!

本部分核心代码如下

while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while,持续跟新栈顶元素
    int mid = st.top();
    st.pop();
    if (!st.empty()) {
        int h = min(height[st.top()], height[i]) - height[mid];
        int w = i - st.top() - 1; // 注意减一,只求中间宽度
        sum += h * w;
    }
}

CPP代码

class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0; // 可以不加
        stack<int> st; // 存着下标,计算的时候用下标对应的柱子高度
        st.push(0);
        int sum = 0;
        for (int i = 1; i < height.size(); i++) {
            if (height[i] < height[st.top()]) {     // 情况一
                st.push(i);
            } if (height[i] == height[st.top()]) {  // 情况二
                st.pop(); // 其实这一句可以不加,效果是一样的,但处理相同的情况的思路却变了。
                st.push(i);
            } else {                                // 情况三
                while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while
                    int mid = st.top();
                    st.pop();
                    if (!st.empty()) {
                        int h = min(height[st.top()], height[i]) - height[mid];
                        int w = i - st.top() - 1; // 注意减一,只求中间宽度
                        sum += h * w;
                    }
                }
                st.push(i);
            }
        }
        return sum;
    }
};

//精简版
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++) {
            while (!st.empty() && height[i] > height[st.top()]) {
                int mid = st.top();
                st.pop();
                if (!st.empty()) {
                    int h = min(height[st.top()], height[i]) - height[mid];
                    int w = i - st.top() - 1;
                    sum += h * w;
                }
            }
            st.push(i);
        }
        return sum;
    }
};
  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值