代码随想录 11.20 || 单调栈 LeetCode 503.下一个更大元素Ⅱ、42.接雨水

503.下一个更大元素Ⅱ

        本题与 496.下一个更大元素Ⅰ 如出一辙,求在给定一维数组中,每个元素右起第一个更大元素。不同之点在于,本题要求将一维数组视为首尾相连的,最后一个元素的下一个元素是第一个元素。问题的重点在于 如何使用单调栈处理循环数组。

    方法一:将原数组扩容到原来的两倍,即将 nums 拼接成 nums + nums,这样 nums[nums.size() - 1] 后面的元素就是 nums[0],数组在物理和逻辑上都变为循环的。然后,使用一个单调栈求解每个元素的下一个更大元素即可。

class Solution {
public:
    vector nextGreaterElements(vector &nums) {
        vector nums1(nums.begin(), nums.end());
        nums.insert(nums.end(), nums1.begin(), nums1.end());

        vector result(nums.size(), -1);
        stack st;

        for (int i = 0; i < nums.size(); ++i) {
            while (!st.empty() && nums[i] > nums[st.top()]) {
                result[st.top()] = nums[i];
                st.pop();
            }
            
            st.push(i);
        }

        result.resize(nums.size() / 2);
        return result;
    }
};

        在上述代码中,我们将 nums数组的内容和长度扩容为原来的两倍。使用一个长度为扩容完后的 nums.size() 的容器作为结果集 result,在使用单调栈更新 result 后,需要将结果集 result resize 至原来的一半,因为在 result 中有一半的内容是重复的。

        方法二:事实上,扩容 nums 是为了能够在逻辑上使 nums[nums.size()] 和 nums[0] 相邻,而不必在物理上使元素也相邻,我们只要在遍历至末尾时,能够重新开始二次遍历 nums,即模拟走了两边数组。

class Solution {
public:
    vector nextGreaterElements(vector &nums) {
        int len = nums.size();

        vector result(len, -1);
        stack st;

        for (int i = 0; i < len * 2; ++i) {
            while (!st.empty() && nums[i % len] > nums[st.top()]) {
                result[st.top()] = nums[i % len];
                st.pop();
            }

            st.push(i % len);
        }

        return result;
    }
};

        在上述代码中,在 for 循环中遍历两倍长度的数组,使用 i % nums.size() 更新 result,事实上,单调栈中,nums 中的每个元素被压栈两次,result 结果集中的每个元素被更新了两次,但是每次的更新内容是一样的。这样空间复杂度减少为 O(1),没有使用到额外的空间,减少代码冗余。

42.接雨水

        给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。很复杂,我只能说很复杂。我们求每根柱子能装水的多少,然后将数量累加,即为给定数组能够装水的数量。每根柱子装水的容量,为以当前柱子为底,以两边最近的更高柱子为边形成的凹槽的容量。根据木桶的短板原理,木桶能装水的多少取决于最短的板子的长度,在本题中,一个凹槽能装水的多少取决于两个相邻的更高的柱子短的那一个。

代码随想录 11.20 || 单调栈 LeetCode 503.下一个更大元素Ⅱ、42.接雨水_第1张图片

           借助 代码随想录中对应章节的图描述,1 号柱子的容量为 0,其左边没有比其高的柱子,无法形成凹槽;2 号柱子的高度为 0,容量为 1,其右边相邻更高的柱子为 3 号,高度为 2,左边相邻更高柱子为 1号,高度为 1。此时,1,2,3 号柱子形成一个凹槽,凹槽的容量为 1 号柱子(比 3 号柱子矮) - 2 号的高度,1 - 0 = 1,此时说的容量其实为高度,还需要求得宽度,宽度为右边柱子的索引 - 左边柱子的索引 - 1,3 - 1 - 1 = 1,所以 2 号柱子的容量为 1 * 1 = 1。依次类推,计算后续每个柱子的容量,从图中可以看出,不是每个柱子都能形成凹槽,存在一些柱子的容量为 0。

        如何将上述过程转换为单调栈,其实就是求数组中每个元素其相邻右边第一个更大元素,和相邻左边第一个更大元素。借助一个单调递增栈,能够求当前元素右边第一个更大元素,此时栈顶元素为当前元素,将栈顶元素出栈之后,新的栈顶元素,即为当前元素左边第一个更大元素。

class Solution {
public:
    int trap(vector &height) {
        int len = height.size();

        int result = 0;
        stack st;

        for (int i = 0; i < len; ++i) {
            while (!st.empty() && height[i] > height[st.top()]) {
                int index = st.top();
                st.pop();
                if (!st.empty()) {
                    int h = min(height[i], height[st.top()]) - height[index];
                    int w = i - st.top() - 1;
                    result += h * w;
                }
            }
            st.push(i);
        }

        return result;
    }
};

你可能感兴趣的:(leetcode,算法,数据结构)