leetcode刷题(单调栈)

通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了,时间复杂度为O(n)。

  1. 每日温度

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指在第 i 天之后,才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

# python
class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        res = [0] * len(temperatures)
        stack = [0]
        for i in range(1, len(temperatures)):
            if temperatures[i] <= temperatures[stack[-1]]:  # 比栈中元素小,直接入栈
                 stack.append(i)
            else:
                while len(stack) != 0 and temperatures[i] > temperatures[stack[-1]]:
                    res[stack[-1]] = i - stack[-1]  # 从后往前找到首个递增距离,出栈
                    stack.pop()
                stack.append(i) # 比栈中剩下的元素小,继续入栈
        return res
// Java
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] res = new int[temperatures.length];
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        for (int i = 1; i < temperatures.length; i++) {
            if (temperatures[i] <= temperatures[stack.peek()]) {
                stack.push(i);
            } else {
                while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                    int preIndex = stack.pop();
                    res[preIndex] = i - preIndex;
                }
                stack.push(i);
            }
        }
        return res;
    }
}
  1. 下一个更大元素I

nums1 中数字 x下一个更大元素 是指 xnums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1nums2 ,下标从 0 开始计数,其中nums1nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

示例 1:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:

  • 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
  • 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
  • 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
# python
class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        res = [-1] * len(nums1)
        stack = [0]
        for i in range(1, len(nums2)):
            if nums2[i] <= nums2[stack[-1]]:
                stack.append(i)
            else:
                while len(stack) != 0 and nums2[i] > nums2[stack[-1]]:  # 先找出比栈顶元素大的索引
                    if nums2[stack[-1]] in nums1:   # 如果该索引的值在num1中
                        index = nums1.index(nums2[stack[-1]])
                        res[index] = nums2[i]       # 记录该结果
                    stack.pop()
                stack.append(i)
        return res

// Java
class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int[] res = new int[nums1.length];
        Arrays.fill(res, -1);
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        for (int i = 0; i < nums1.length; i++) {
            hashMap.put(nums1[i], i);
        }
        for (int i = 1; i < nums2.length; i++) {
            if (nums2[i] <= nums2[stack.peek()]) {
                stack.push(i);
            } else {
                while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {
                    int index = stack.pop();
                    if (hashMap.containsKey(nums2[index])) {
                        res[hashMap.get(nums2[index])] = nums2[i];
                    }
                }
                stack.push(i);
            }
        }
        return res;
    }
}
  1. 下一个更大元素II

给定一个循环数组 numsnums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1

示例 1:
输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

# python
class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        n = len(nums)
        res = [-1] * n
        stack = [0] # 存放0 ~ n-1的索引
        for i in range (1, 2 * n):  # 将nums数组往后复制一份,模拟环形
            while len(stack) != 0 and nums[i % n] > nums[stack[-1]]:    # i得取模找到元素值
                index = stack.pop()     # 找到第一个比栈顶元素大的值,出栈
                res[index] = nums[i % n]    # 把下一个更大元素保存到结果中
            stack.append(i % n)             # 入栈得把索引变回0~n-1
        return res
// Java
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        int[] res = new int[n];
        Arrays.fill(res, -1);
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        for (int i = 1; i < 2 * n; i++) {
            while (!stack.isEmpty() && nums[i % n] > nums[stack.peek()]) {
                int index = stack.pop();
                res[index] = nums[i % n];
            }
            stack.push(i % n);
        }
        return res;
    }
}
  1. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:
在这里插入图片描述

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

# python
class Solution:
    def trap(self, height: List[int]) -> int:
        stack = [0]
        res = 0
        for i in range(1, len(height)):
            if height[i] < height[stack[-1]]:
                stack.append(i) # 当前元素比栈顶小,直接入栈
            elif height[i] == height[stack[-1]]:
                stack.pop()     # 两个柱子高度一样,左边的柱子不可能存水,出栈
                stack.append(i) # 将新的柱子入栈
            else:
                while (len(stack) != 0 and height[i] > height[stack[-1]]):
                    mid = stack.pop()   # 栈中都是递增元素,把栈顶元素出栈,作为中间索引
                    if len(stack) != 0: # 如果左边没有柱子,则无法存水
                        h = min(height[stack[-1]], height[i]) - height[mid] # 左右最小的为最高点
                        w = i - stack[-1] - 1   # 由于宽度可能是多个相同高度柱子连在一起的
                        res += h * w 
                stack.append(i)
        return res
// Java
class Solution {
    public int trap(int[] height) {
        int n = height.length;
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        int res = 0;
        for (int i = 1; i < n; i++) {
            if (height[i] < height[stack.peek()]) {
                stack.push(i);
            } else if (height[i] == height[stack.peek()]) {
                stack.pop();
                stack.push(i);
            } else {
                while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
                    int mid = stack.pop();
                    if (!stack.isEmpty()) {
                        int h = Math.min(height[stack.peek()],height[i]) - height[mid];
                        int w = i - stack.peek() - 1;
                        res += h * w;
                    }
                }
                stack.push(i);
            }
        }
        return res;
    }
}
  1. 柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1

求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:
在这里插入图片描述

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

# python
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        '''
        找每个柱子左右侧的第一个高度值小于该柱子的柱子
        单调栈:栈顶到栈底:从大到小(每插入一个新的小数值时,都要弹出先前的大数值)
        栈顶,栈顶的下一个元素,即将入栈的元素:这三个元素组成了最大面积的高度和宽度
        '''
        # 本题原首尾的两个柱子可以作为核心柱进行最大面积尝试,故在首尾各加上一个0
        heights.insert(0, 0)
        heights.append(0)
        stack = [0]
        res = 0
        for i in range(1, len(heights)):
            if heights[i] > heights[stack[-1]]: # 当前元素比栈顶元素大,入栈,保持栈元素递增
                stack.append(i)
            elif heights[i] == heights[stack[-1]]:  # 高度相同取一个就行,避免宽度重复计算
                stack.pop()         # 高度相同,只记录最新的索引
                stack.append(i)     # 计算面积时宽减去低一级的索引把所有的同高度的宽都算上了
            else:
                # 当前元素比栈顶元素小,往前计算最大面积,计算完出栈后再接着入栈,始终保持递增
                while (len(stack) != 0 and heights[i] < heights[stack[-1]]):
                    mid = stack.pop()   # 把当前最高的索引取出
                    # if stack:
                    if len(stack) != 0: # 第一次计算的面积是最高h * 1
                        w = i - stack[-1] - 1   # 接着是次高h * 2, 以此递推
                        h = heights[mid]        # 最终取其中围成的最大值
                        res = max(res, w * h)
                stack.append(i)
        return res

// Java
class Solution {
    public int largestRectangleArea(int[] heights) {
        int[] newHeights = new int[heights.length + 2];
        newHeights[0] = 0;
        newHeights[newHeights.length - 1] = 0;
        for (int i = 1; i < newHeights.length - 1; i++) {
            newHeights[i] = heights[i - 1];
        }
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        heights = newHeights;
        int res = 0;
        for (int i = 1; i < heights.length; i++) {
            if (heights[i] > heights[stack.peek()]) {
                stack.push(i);
            } else if (heights[i] == heights[stack.peek()]) {
                stack.pop();
                stack.push(i);
            } else {
                while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                    int mid = stack.pop();
                    if (!stack.isEmpty()) {
                        int h = heights[mid];
                        int w = i - stack.peek() - 1;
                        res = Math.max(res, h * w);
                    }
                }
                stack.push(i);
            }
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天涯小才

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值