代码随想录算法训练营day59|第十章 单调栈part02

目录

503.下一个更大元素II 

42. 接雨水  


 

503.下一个更大元素II 

这道题和 739. 每日温度 几乎如出一辙,可以自己尝试做一做

 

代码随想录

这道题可以直接拼接数组,但是显然使用取余运算更划算。注意这回 i 代表的不直接是nums的下标,而更多的是遍历次数,取余之后才会是nums下标。还有文章里面给出的for的循环次数是nums.size()*2-1,而实际上可以是nums.size()*2-2,因为之所以要遍历两遍nums,是为了让所有的元素找临近较大值的时候可以从它的左边转一圈找到它的右边,所以针对最后后一个元素的寻找(其他元素早就满足这种寻找了),可以不遍历最后一个元素本身。

vector<int> nextGreaterElements(vector<int>& nums) {
        stack<int> st;
        vector<int> res(nums.size(),-1);
        st.push(0);
        for(int i=1;i<nums.size()*2-1;i++){
            while(st.size()&&nums[i%nums.size()]>nums[st.top()]){
                res[st.top()]=nums[i%nums.size()];
                st.pop();
            }
            st.push(i%nums.size());
        }
        return res;
    }

42. 接雨水  

接雨水这道题目是 面试中特别高频的一道题,也是单调栈 应用的题目,大家好好做做。建议是掌握 双指针 和单调栈,因为在面试中 写出单调栈可能 有点难度,但双指针思路更直接一些。在时间紧张的情况有,能写出双指针法也是不错的,然后可以和面试官在慢慢讨论如何优化。 

代码随想录

这道题使用双指针的思路其实是遍历两遍height数组,分别找到它的右边最高的柱子和左边最高的柱子,然后取这两个的最小值,这个值就是填满水之后应该达到的高度,如果这个高度是正值那就加上,反之说明不合法则不予理会,以此来求当前下标处增加的水量。

遍历求右边最高柱子的时候要从右往左遍历,如果当前的柱子高度小于历史最高柱子的高度,那就把历史最高柱子高度赋值给它,反之如果大于,那就证明当前柱子的右边没有比它更高的柱子了,于是这个下标处也不会盛水,所以为了依旧套用递推公式,这个地方应该赋值为当前柱子的高度(在使用递推公式推导的时候,需要求最小值,这个最小值有可能是当前柱子的高度,那么计算出来值就会是0,而如果是最左边柱子的高度,那么这个值就会是负数,而因为不合法,所以不被加入)。于是推导公式就简化为取当前柱子高度和历史柱子最高高度的最大值。求左边最高柱子数组差不多一个道理,只是需要倒序遍历。注意,可以事先设置这个最大高度为0,然后遍历就可以从两端开始,或者也可以直接把两端的值设置为两端柱子的高度,这样可以少遍历一个值,其实大差不差。

b5f475ca069b4318ac336879f69547c6.png

int trap(vector<int>& height) {
        if (height.size() <= 2) return 0;
        vector<int> maxLeft(height.size(), 0);
        vector<int> maxRight(height.size(), 0);
        int size = maxRight.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;
    }

这道题使用单调栈做法也比较复杂,因为单调栈每次会弹出一些元素,所以不能像双指针做法那样按列来求,而是要一行一行的求,也就是除了求增加水量的高度之外,还要求增加水量的宽度(上一题直接求高度代表水量是因为宽度默认为1了)。

每次如果遍历到大于栈顶元素的值,那就先记录一下当前的栈顶元素,然后再弹出这个栈顶元素,然后再判断一下这个栈里面是否还剩有元素,如果剩有元素,证明可能构成一个凹槽用来盛水,那就要计算盛水的水量,先计算水的高度,水的高度取遍历的柱子的高度和栈顶高度的最小值再减去之前储存的那个栈顶元素,水的宽度当然就是遍历的元素的下标和现在栈顶元素的下标的下标差再减一(因为求的是中间的部分,用来盛水的那种宽度),相乘求得水量;而如果没有元素剩余,那就证明不能盛水。无论能否盛水,都需要将当前元素压入栈,以便后来的判断。如果遍历到小于等于栈顶元素的值,那就直接压入栈。

其实便利到等于栈顶元素的值有两种处理方法,第一种方法是直接压入栈中,然后按上面的做法来处理;另一种做法是舍弃掉这个元素,其实就算不舍弃压入栈中,最后公式计算得到的增加水量也是零,所以没什么太大差别。

afe86d9a5d7e46df9c4a658a30758479.png

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;
    }

 

 

  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值