leetcode classification test questions: Stack (3. The next larger number)

1. The "Stack (3. Next larger number)" type summarized in this blog specifically refers to finding the position of the first element on the right/left of any element that is larger/smaller than itself . This is consistent with The first-in-last-out idea of ​​the stack is consistent.
2. The algorithm template used to deal with this kind of problem using a stack is often called a monotonic stack . This is because after the code for this kind of problem is written, the elements in the stack that happen to be operated satisfy the law of monotonous increase or decrease, which is essentially a problem. The type of questions conforms to the first-in-last-out concept of the stack, so the stack is used to process it. It is not very appropriate to set the template according to the monotonic stack first: there are question types that need to be processed with the stack, and there happens to be a monotonic stack, not the monotonic stack. To apply problem solving

739. Daily temperature

The basic question of the next larger number : forward traversal can be divided into three situations: the stack is empty and the traversal index is added to the stack ; the element less than the top of the stack is traversed and the index is added to the stack ; the element greater than the top of the stack satisfies the question and continues to be popped out of the stack - —I feel that the monotonic stack is formed due to this kind of processing, and the monotonic stack template is not determined first.

from typing import List
'''
739. 每日温度
给定一个整数数组temperatures,表示每天的温度,返回一个数组answer,其中answer[i]是指对于第 i 天,下一个更高温度出现在几天后。
如果气温在这之后都不会升高,请在该位置用0 来代替
示例 1:
    输入: temperatures = [73,74,75,71,69,72,76,73]
    输出: [1,1,4,2,1,1,0,0]
题眼:寻找任一个元素的右边/左边第一个比自己大/小的元素的位置,与栈的先入后出思想一致
思路:正向遍历,分三种情况:栈为空遍历索引入栈;小于栈顶元素遍历索引入栈;大于栈顶元素则满足题意持续出栈——感觉单调栈是因为这种处理形成的,并不是先确定
单调栈模板的。
'''


class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        result = [0] * len(temperatures)
        stk = []
        for i in range(len(temperatures)):
            if len(stk) == 0:  # 栈为空,则让索引入栈
                stk.append(i)
            else:
                if temperatures[i] <= temperatures[stk[-1]]:  # 入栈:当前元素小于等于栈顶元素
                    stk.append(i)
                else:
                    while len(stk) > 0 and temperatures[i] > temperatures[stk[-1]]:  # 出栈:当前元素大于栈顶元素
                        result[stk[-1]] = i - stk[-1]
                        stk.pop()
                    stk.append(i)
            i += 1
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            temperatures = [int(n) for n in in_line[1].strip()[1: -1].split(',')]
            print(obj.dailyTemperatures(temperatures))
        except EOFError:
            break

496. Next Greater Element I

The basic question of the next larger number : here the elements in the sequence are not repeated, then the elements and indexes are unique identifiers (the simplest question type of binary search is also the elements are not repeated), and it is simpler.

from typing import List
'''
496. 下一个更大元素 I
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 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 。
题眼:寻找任一个元素的右边/左边第一个比自己大/小的元素的位置,与栈的先入后出思想一致
思路:1、遍历nums2建立下一个更大元素的hashTable(元素不重复才能在这里用哈希表)——不重复也意味着元素和索引都是唯一标识
     2、遍历nums1寻找结果
'''


class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 第一步,建立nums2中下一个更大元素的哈希表
        hashTable = {
    
    }
        stk = []  # 定义栈之后,思考下入栈的是索引还是元素,这里元素不重复意味着索引与元素都是唯一标识
        for i in range(len(nums2)):
            if len(stk) == 0:
                stk.append(i)
            else:
                if nums2[i] < nums2[stk[-1]]:  # 无重复,所以不用加等于
                    stk.append(i)
                else:
                    while len(stk) > 0 and nums2[i] > nums2[stk[-1]]:
                        hashTable[nums2[stk[-1]]] = nums2[i]
                        stk.pop()
                    stk.append(i)
        # 第二步,建立结果数组
        result = [-1] * len(nums1)
        for i in range(len(nums1)):
            if nums1[i] in hashTable:
                result[i] = hashTable[nums1[i]]
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            in_line1 = in_line[1].split('[')[1].split(']')[0]
            in_line2 = in_line[2].split('[')[1].split(']')[0]
            nums1 = [int(n) for n in in_line1.split(',')]
            nums2 = [int(n) for n in in_line2.split(',')]
            print(obj.nextGreaterElement(nums1, nums2))
        except EOFError:
            break

503. The Next Greater Element II

The basic question of the next larger number : here requires the operation of the loop array, and with the experience of the leetcode classification question: string matching in KMP algorithm 686. Repeated superposition of string matching and loop matching, this question is not difficult at all. : Loop through the array twice without accessing the last element a second time.

from typing import List
'''
503. 下一个更大元素 II
给定一个循环数组nums(nums[nums.length - 1]的下一个元素是nums[0]),返回nums中每个元素的 下一个更大元素 。
数字 x的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
    输入: nums = [1,2,1]
    输出: [2,-1,2]
    解释: 第一个 1 的下一个更大的数是 2;
    数字 2 找不到下一个更大的数;
    第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
题眼:寻找任一个元素的右边/左边第一个比自己大/小的元素的位置,与栈的先入后出思想一致
思路:循环两次遍历数组,且不用对最后一个元素访问第二次
'''


class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        # 循环两次遍历数组,且不用对最后一个元素访问第二次
        stk = []
        result = [-1] * len(nums)
        for i in range(len(nums) * 2 - 1):
            if len(stk) == 0:
                stk.append(i % len(nums))
            else:
                if nums[i % len(nums)] <= nums[stk[-1]]:
                    stk.append(i % len(nums))
                else:
                    while len(stk) > 0 and nums[i % len(nums)] > nums[stk[-1]]:
                        result[stk[-1]] = nums[i % len(nums)]
                        stk.pop()
                    stk.append(i % len(nums))
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            in_line1 = in_line[1].split('[')[1].split(']')[0]
            nums = [int(n) for n in in_line1.split(',')]
            print(obj.nextGreaterElements(nums))
        except EOFError:
            break

42. Catching rainwater

1. Variation question of the next larger number , that is, the next largest number on the left and right sides : The most critical part of this question is to understand the question. The amount of rainwater received at each position depends on the maximum value on the left and right sides , so according to This idea defines two arrays, saves the maximum value of the left and right sides respectively, and then traverses the sequence again to accumulate the rainwater amount.
2. This question can also be solved according to the monotonic stack method: find the sandwiched area according to the "large and small" rule. , consistent with the first-in-last-out idea of ​​the stack: if it is less than the top element of the stack, it is pushed into the stack; if it is equal to the top element of the stack, it replaces the top element of the stack; if it is greater than the top element of the stack, it is judged to be "large" to calculate the area and calculate the amount of rainwater in behavioral units 3. This
way The problem can also be solved using a double pointer approach: the left and right pointers traverse from both sides at the same time, and maintain the maximum value marks on the left and right sides respectively. Whichever pointer corresponds to the smaller element will be updated, which is equivalent to which pointer can be based on the two maximum values. Mark and calculate the amount of water received,
just move which pointer; this idea is clever but more difficult, not too conventional

from typing import List
'''
42. 接雨水
给定 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 个单位的雨水(蓝色部分表示雨水)。
题眼:
思路1、模拟:每个位置接雨水的量取决于左右两边的最大值,因此,定义两个数组,分别保存左右两边的最大值(包括自己,闭区间考虑);这种思路是按照列为单位求
雨水量的,简单直观,建议掌握!
思路2、单调栈:按照“大小大”规律求夹着的面积,与栈的先入后出思想一致:小于栈顶元素入栈;等于栈顶元素则替换栈顶元素;大于栈顶元素则判断“大小大”计算面积;
这种思路按照行为单位求雨水量的,这种解释不是太好理解,还不如按照括号配对的思路理解,按照这种思路有点难
思路3、双指针:左右指针从两侧同时遍历,并分别维护左右两侧的最大值标记,哪个指针对应的元素小就更新哪个,等价于哪个指针可以根据两个最大值标记计算接水量,
就移动哪个指针;这个思路巧妙但更难了,不是太常规,理解起来有点吃力
'''


class Solution:
    def trap(self, height: List[int]) -> int:
        # # 思路1、模拟:每个位置接雨水的量取决于左右两边的最大值,因此,定义两个数组,分别保存左右两边的最大值(包括自己,闭区间考虑)
        # lmax = [0] * len(height)
        # maxNum = 0
        # for i in range(len(height)):
        #     maxNum = max(maxNum, height[i])
        #     lmax[i] = maxNum
        # rmax = [0] * len(height)
        # maxNum = 0
        # for i in range(len(height) - 1, -1, -1):
        #     maxNum = max(maxNum, height[i])
        #     rmax[i] = maxNum
        # result = 0
        # for i in range(len(height)):
        #     result += min(lmax[i], rmax[i]) - height[i]
        # return result

        # # 思路2、单调栈:按照“大小大”规律求夹着的面积,与栈的先入后出思想一致:小于栈顶元素入栈;等于栈顶元素则替换栈顶元素;
        # # 大于栈顶元素则判断“大小大”计算面积
        # stk = []
        # result = 0
        # for i in range(len(height)):
        #     if len(stk) == 0:
        #         stk.append(i)
        #     else:
        #         if height[i] < height[stk[-1]]:
        #             stk.append(i)
        #         elif height[i] == height[stk[-1]]:  # 这一步可以注释掉,合并到上一步入栈
        #             stk.pop()
        #             stk.append(i)
        #         elif height[i] > height[stk[-1]]:
        #             while len(stk) > 0 and height[i] > height[stk[-1]]:
        #                 right = height[i]
        #                 mid = height[stk.pop()]
        #                 if len(stk) > 0:
        #                     left = height[stk[-1]]
        #                     result += (min(left, right) - mid) * (i - stk[-1] - 1)
        #             stk.append(i)
        # return result

        # 思路3、双指针:左右指针从两侧同时遍历,并分别维护左右两侧的最大值标记,哪个指针对应的元素小就更新哪个,等价于哪个指针可以根据两个最大值标
        # 记计算接水量,就移动哪个指针
        left, right = 0, len(height) - 1
        lMax, rMax = height[left], height[right]  # lMax标记了left位置的左侧最大值(包括left本身);rMax标记了right位置的右侧最大值
        # (包括right本身)
        result = 0
        while left < right:  # left==right时,表示定位到了数组中的最大值处了,这里不用计算,肯定不能接水
            if height[left] <= height[right]:  # 情况1:height[left]刚好为lMax,必有lMax<=rMax;情况2:height[left]之前的某个数
                # 为lMax,那么当时left能更新的条件必然是lMax<=rMax;那么在left位置,必然有lMax<=left位置自己的右侧最大值(本来就比rMax大)
                # 所以,left指针可以记计算接水量
                result += lMax - height[left]  # 因为lMax包含了left位置,所以不用担心该计算小于0
                left += 1
                lMax = max(lMax, height[left])
            elif height[left] > height[right]:  # 情况1:height[right]刚好为rMax,必有lMax>rMax;情况2:height[right]之前的某个数
                # 为lMax,那么当时right能更新的条件必然是lMax>rMax;那么在right位置,必然有right位置自己的左侧最大值(本来就比lMax大)>rMax
                # 所以,right指针可以记计算接水量
                result += rMax - height[right]  # 因为rMax包含了right位置,所以不用担心该计算小于0
                right -= 1
                rMax = max(rMax, height[right])
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            height = [int(n) for n in in_line[1].strip()[1: -1].split(',')]
            print(obj.trap(height))
        except EOFError:
            break

84. The largest rectangle in a bar chart

1. Variation question of the next larger number , that is, the next smaller number on the left and right sides : The most critical part of this question is also to understand the question. The maximum rectangular area of ​​each position depends on the first one on the left and right sides. For indexes that are smaller than the current value , use two monotonic stacks to find the first index on the left and right sides of each position that is smaller than the current value, and then traverse the original sequence again to find the maximum rectangular area of ​​each position; this idea is simple and intuitive 2
. This question can also be solved according to the monotonic stack idea: look for "small size" combinations to pop out of the stack: push the element larger than the top of the stack; replace it when it is equal to the top element of the stack (the maximum area is the same as the top element of the stack); smaller than the top of the stack The elements are popped off the stack to find the maximum rectangular area; finally, the remaining elements in the stack are also processed.

from typing import List
'''
84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
    输入:heights = [2,1,5,6,2,3]
    输出:10
    解释:最大的矩形为图中红色区域,面积为 10
题眼:读懂题很重要,每个位置的最大矩形面积取决于左右两侧第一个小于当前值的索引
思路1、模拟:分别用两个单调栈求解每个位置的左右两侧第一个小于当前值的索引,然后再次遍历原序列求每个位置的最大矩形面积;这种思路简单直观!
思路2、单调栈:寻找“小大小”的组合进行出栈:大于栈顶元素入栈;等于栈顶元素时进行替换(最大面积与栈顶元素相同);小于栈顶元素出栈求最大矩形面积;最后把栈内
剩余元素也处理完
'''


class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        # # 思路1、模拟:分别用两个单调栈求解每个位置的左右两侧第一个小于当前值的索引,然后再次遍历原序列求每个位置的最大矩形面积
        # rmin = [len(heights)] * len(heights)
        # stk = []
        # for i in range(len(heights)):
        #     if len(stk) == 0:
        #         stk.append(i)
        #     else:
        #         if heights[i] >= heights[stk[-1]]:
        #             stk.append(i)
        #         else:
        #             while len(stk) > 0 and heights[i] < heights[stk[-1]]:
        #                 rmin[stk[-1]] = i
        #                 stk.pop()
        #             stk.append(i)
        #
        # lmin = [-1] * len(heights)
        # stk = []
        # for i in range(len(heights) - 1, -1, -1):
        #     if len(stk) == 0:
        #         stk.append(i)
        #     else:
        #         if heights[i] >= heights[stk[-1]]:
        #             stk.append(i)
        #         else:
        #             while len(stk) > 0 and heights[i] < heights[stk[-1]]:
        #                 lmin[stk[-1]] = i
        #                 stk.pop()
        #             stk.append(i)
        # result = 0
        # for i in range(len(heights)):
        #     result = max(result, heights[i] * (rmin[i] - lmin[i] - 1))
        # return result

        # 思路2、单调栈:寻找“小大小”的组合进行出栈:大于栈顶元素入栈;等于栈顶元素时进行替换(最大面积与栈顶元素相同);小于栈顶元素出栈求最大矩形
        # 面积;最后把栈内剩余元素也处理完
        stk = []
        result = 0
        for i in range(len(heights)):
            if len(stk) == 0:
                stk.append(i)
            else:
                if heights[i] > heights[stk[-1]]:
                    stk.append(i)
                elif heights[i] == heights[stk[-1]]:  # 这一步可以注释掉,合并到上一步入栈
                    stk.pop()
                    stk.append(i)
                elif heights[i] < heights[stk[-1]]:
                    while len(stk) > 0 and heights[i] < heights[stk[-1]]:
                        mid = stk.pop()
                        left = -1 if len(stk) == 0 else stk[-1]
                        result = max(result, heights[mid] * (i - left - 1))
                    stk.append(i)
        # 最后把栈内剩余元素也处理完
        while len(stk) > 0:
            mid = stk.pop()
            left = -1 if len(stk) == 0 else stk[-1]
            result = max(result, heights[mid] * (len(heights) - left - 1))
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            in_line1 = in_line[1].split('[')[1].split(']')[0]
            nums = [int(n) for n in in_line1.split(',')]
            print(obj.largestRectangleArea(nums))
        except EOFError:
            break

85. Maximum rectangle

This question is indeed too difficult. I would never have thought of it without looking at the solution: You need to first count the number of consecutive 1's based on each row , and convert each row into a solution template for "84. The largest rectangle in the histogram", refer to The boss’s solution

from typing import List
'''
85. 最大矩形
给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
示例 1:
    输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
    输出:6
思路:这道题确实太难了,不看题解根本想不到:需要先以每行为基准,统计连续1的个数,转换为“84. 柱状图中最大的矩形”的求解模板
'''


class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        if len(matrix) == 0:
            return 0
        # 第一步,先以每行为基准,统计连续1的个数,转换为柱状图的形式
        m, n = len(matrix), len(matrix[0])
        matrix2 = [[0] * n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if i == 0:
                    if matrix[i][j] == '1':
                        matrix2[i][j] = 1
                else:
                    if matrix[i][j] == '1':
                        matrix2[i][j] = matrix2[i-1][j] + 1
        # 第二步,按照“84. 柱状图中最大的矩形”的求解模板对每一行进行求解
        result = 0
        for i in range(m):
            # 1、求右侧下一个更小的索引
            rmin = [n] * n
            stk = []
            for j in range(n):
                if len(stk) == 0:
                    stk.append(j)
                else:
                    if matrix2[i][j] >= matrix2[i][stk[-1]]:
                        stk.append(j)
                    else:
                        while len(stk) > 0 and matrix2[i][j] < matrix2[i][stk[-1]]:
                            rmin[stk[-1]] = j
                            stk.pop()
                        stk.append(j)
            # 2、求左侧下一个更小的索引
            lmin = [-1] * n
            stk = []
            for j in range(n - 1, -1, -1):
                if len(stk) == 0:
                    stk.append(j)
                else:
                    if matrix2[i][j] >= matrix2[i][stk[-1]]:
                        stk.append(j)
                    else:
                        while len(stk) > 0 and matrix2[i][j] < matrix2[i][stk[-1]]:
                            lmin[stk[-1]] = j
                            stk.pop()
                        stk.append(j)
            # 3、求每个遍历元素的最大矩形面积
            for j in range(n):
                result = max(result, matrix2[i][j] * (rmin[j] - lmin[j] - 1))

        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            matrix = []
            if in_line[1].strip()[1: -1] == "":
                matrix = []
            else:
                for s in in_line[1].strip()[1: -1].split(']')[: -1]:
                    row = []
                    for ch in s.split('[')[1].split(','):
                        row.append(ch[1: -1])
                    matrix.append(row)
            print(obj.maximalRectangle(matrix))
        except EOFError:
            break

おすすめ

転載: blog.csdn.net/qq_39975984/article/details/132803067