单调栈经典问题整理

下一个更大的问题

1、下一个更大元素 I

class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        m, stack = defaultdict(lambda: -1), deque()
        for num in nums2:
            while stack and stack[-1] < num:
                cur = stack.pop()
                m[cur] = num
            stack.append(num)
        res = []
        for num in nums1:
            res.append(m[num])
        return res 

2、下一个更大元素 II

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        nums2 = nums * 2
        m, stack = defaultdict(lambda: -1), deque()
        for i, num in enumerate(nums2):
            while stack and nums2[stack[-1]] < num:
                cur = stack.pop()
                m[cur] = num
            stack.append(i)
        res = []
        for i,num in enumerate(nums):
            res.append(m[i])
        return res

3、下一个更大元素 IV

class Solution:
    def secondGreaterElement(self, nums: List[int]) -> List[int]:
        res, first, second = [-1] * len(nums), [], deque()
        for i, x in enumerate(nums):
            temp = deque()
            while second and nums[second[-1]] < x:
                res[second.pop()] = x
            while first and nums[first[-1]] < x:
                temp.append(first.pop())
            while temp:
                second.append(temp.pop())
            first.append(i)
        return res

4、每日温度

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        res, stack = [0] * len(temperatures), deque()
        temperatures = [0] + temperatures
        for i, t in enumerate(temperatures):
            while stack and temperatures[stack[-1]] < t:
                cur = stack.pop()
                if cur > 0:
                    res[cur - 1] = i - cur
            stack.append(i)
        return res

单调栈与二叉树

1、验证前序遍历的二叉搜索树

class Solution:
    def verifyPreorder(self, preorder: List[int]) -> bool:
        minl, st = 0, deque()
        for num in preorder:
            if num < minl: return False
            while st and num > st[-1]:
                minl = st[-1]
                st.pop()
            st.append(num)
        return True

2、前序遍历构造二叉搜索树

class Solution:
    def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        st, res = deque(), None
        for val in preorder:
            cur = None
            while st and val > st[-1].val:
                cur = st.pop()
            if cur: 
                cur.right = TreeNode(val)
                st.append(cur.right)
            else:
                nd = TreeNode(val)
                if st: st[-1].left = nd
                else: res = nd
                st.append(nd)
        return res

 3、叶值的最小代价生成树

class Solution:
    def mctFromLeafValues(self, arr: List[int]) -> int:
        stack = []
        mct = 0
        for num in arr:
            while stack and num > stack[-1]:
                min_1 = stack.pop()
                if stack:
                    min_2 = min(stack[-1],num)
                else:
                    min_2 = num
                mct = mct + min_1 * min_2
            
            stack.append(num)
        
        while len(stack) > 1:
            mct = mct + stack.pop() * stack[-1]

        return mct; 

4、给定二叉搜索树的插入顺序求深度 

class Solution:
    def maxDepthBST(self, order: List[int]) -> int:
        n = len(order)
        father, occur, stack = [0] * (n + 1), [0] * (n + 1), []
        for i, x in enumerate(order, 1): occur[x] = i

        for x, i in enumerate(occur):
            while stack and occur[stack[-1]] > i:
                if occur[father[stack[-1]]] < i:
                    father[stack[-1]] = x
                stack.pop()
            if stack:
                father[x] = stack[-1]
            stack.append(x)
        
        for x in order:
            father[x] = 1 + father[father[x]]

        return max(father)

图形单调栈

1、接雨水

class Solution:
    def trap(self, heights: List[int]) -> int:
        stack, res = [], 0
        for i in range(len(heights)):
            while stack and heights[stack[-1]] < heights[i]:
                cur = stack.pop()
                if not stack: break
                res += (min(heights[stack[-1]], heights[i]) - heights[cur]) * (i - stack[-1] - 1)
            stack.append(i)
        return res

2、柱状图中最大的矩形

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        stack, heights = [], [0] + heights + [0]
        res = 0
        for i in range(len(heights)):
            while stack and heights[stack[-1]] > heights[i]:
                cur = stack.pop()
                res = max(res, (i - stack[-1] - 1)* heights[cur])
            stack.append(i)
        return res

3、统计全 1 子矩形

class Solution:
    def numSubmat(self, mat: List[List[int]]) -> int:
        m, n = len(mat), len(mat[0])
        
        row = [[0] * n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if j == 0:
                    row[i][j] = mat[i][j]
                else:
                    row[i][j] = 0 if mat[i][j] == 0 else row[i][j - 1] + 1
        
        ans = 0
        for j in range(n):
            Q = list()
            total = 0
            for i in range(m):
                length = 1
                while Q and Q[-1][0] > row[i][j]:
                    # 弹出的时候要减去多于的答案
                    total -= Q[-1][1] * (Q[-1][0] - row[i][j])
                    length += Q[-1][1]
                    Q.pop()
                total += row[i][j]
                ans += total
                Q.append((row[i][j], length))

        return ans

 单调栈与数组

1、去除重复字母

class Solution:
    def removeDuplicateLetters(self, s: str) -> str:
        n, stack = len(s), []
        for i in range(n):
            if s[i] in stack:
                continue
            else:
                while stack and stack[-1] > s[i] and stack[-1] in s[i+1:]:
                    stack.pop()
                stack.append(s[i])
            
        return "".join(stack)

2、132 模式

class Solution:
    def find132pattern(self, nums: List[int]) -> bool:
        s = deque([])
        k = -float('inf')
        for n in reversed(nums):
            if n < k: return True
            while s and n > s[-1]:
                k = max(k, s.pop())
            s.append(n)
        return False

3、最多能完成排序的块

class Solution:
    def maxChunksToSorted(self, arr: List[int]) -> int:
        #[1,2,0,3] #[2,0,1]
        n = len(arr)
        nums = arr
        stack=[]
        for i in range(n):
            m = nums[i]
            while stack and stack[-1] > nums[i]:
                m=max(stack[-1],m)
                stack.pop(-1)
            stack.append(m)
        return len(stack)

4、股票价格跨度

class StockSpanner:

    def __init__(self):
        self.st = deque()

    def next(self, price: int) -> int:
        w = 0
        while self.st and self.st[-1][0] <= price:
            w += self.st.pop()[1]
        self.st.append((price, w + 1))
        return w + 1 

5、子数组的最小值之和

class Solution:
    def sumSubarrayMins(self, arr: List[int]) -> int:
        A.append(-inf)
        stack, res=[-1], 0
        for i in range(len(A)):
            while A[i] < A[stack[-1]]:
                idx = stack.pop()
                res += A[idx] * (i - idx) * (idx - stack[-1])
            stack.append(i)
        return res % (10**9 + 7)

子数组范围和与上面的题类似,只需要再构建单调递减栈求出最大值的和即可 

6、最大宽度坡

class Solution:
    def maxWidthRamp(self, nums: List[int]) -> int:
        st, maxl = deque(), 0
        for i, num in enumerate(nums):
            if not st or num < nums[st[-1]]:
                st.append(i)
        n = len(nums)
        for i in reversed(range(n)):         
            while st and nums[st[-1]] <= nums[i]:
                maxl = max(maxl, i - st.pop())   
        return maxl

7、使数组按非递减顺序排列

class Solution:
    def totalSteps(self, nums: List[int]) -> int:
        ans, st = 0, []
        for num in nums:
            max_t = 0
            while st and st[-1][0] <= num:
                max_t = max(max_t, st.pop()[1])
            if st: max_t += 1
            ans = max(ans, max_t)
            st.append((num, max_t))
        return ans

8、单调栈 + 前缀和 -- 巫师的总力量和

class Solution:
    def totalStrength(self, strength: List[int]) -> int:
        n = len(strength)
        # left[i] 为左侧严格小于 strength[i] 的最近元素位置(不存在时为 -1)
        # right[i] 为右侧小于等于 strength[i] 的最近元素位置(不存在时为 n)
        left, right, st = [-1] * n, [n] * n, []
        for i, v in enumerate(strength):
            while st and strength[st[-1]] >= v: right[st.pop()] = i
            if st: left[i] = st[-1]
            st.append(i)

        ss = list(accumulate(accumulate(strength, initial=0), initial=0))  # 前缀和的前缀和

        ans = 0
        for i, v in enumerate(strength):
            l, r = left[i] + 1, right[i] - 1  # [l, r]  左闭右闭
            tot = (i - l + 1) * (ss[r + 2] - ss[i + 1]) - (r - i + 1) * (ss[i + 1] - ss[l])
            ans += v * tot  # 累加贡献
        return ans % (10 ** 9 + 7)

9、所有子数组最小值中的最大值

思路主要是求最小值的区间和更新最小值两步
一、
1.1 以当前值为最小值,求出能覆盖的最远区间,依次求出左边界和右边界
1.2 相同区间长度的值可能有多个,按题目意思求最大值

二、第一步中可能会存在某个区间长度的值没有求到的情况,有遗漏;
遗漏的时候说明该值和后面较长的区间值相同
由于区间较短的值必定大于等于区间较长的值,因此逆序遍历更新下结果数组

class Solution:
    def findMaximums(self, nums: List[int]) -> List[int]:
        nums = [0] + nums + [0]
        ans = [0] * (len(nums) - 2)
        dic = collections.defaultdict(int)
        stack = []
        for i in range(len(nums)):
            while stack and nums[stack[-1]] > nums[i]:
                index = stack.pop()
                dic[i - stack[-1] - 1]=max(dic[i - stack[-1] - 1], nums[index])
            stack.append(i)
        for k in dic:
            ans[k - 1] = dic[k]
        for i in range(len(ans) - 1, 0, -1):
            ans[i - 1] = max(ans[i-1],ans[i])
        return ans

10、找出最具竞争力的子序列 -- 条件单调栈 

1、弹出栈时考虑剩余元素个数和栈元素个数是否至少有k个
2、压入栈时考虑栈元素不超过k

class Solution:
    def mostCompetitive(self, nums: List[int], k: int) -> List[int]:
        st = deque()
        for i, n in enumerate(nums):
            while st and len(nums) - i + len(st) > k and n < st[-1]:
                st.pop()
            if len(st) < k:  st.append(n)
            
        return list(st)

11、 队列中可以看到的人数 --  逆序单调栈

class Solution:
    def canSeePersonsCount(self, heights: List[int]) -> List[int]:
        n = len(heights)
        res = [0] * n

        st = deque([heights[n - 1]])
        for j in range(n - 2,-1,-1):
            v = heights[j]
            while st and v > st[-1]:
                res[j] += 1
                st.pop()
            if st: res[j] += 1
            st.append(v)
        
        return res

单调队列

1、带限制的子序列和

class Solution:
    def constrainedSubsetSum(self, nums: List[int], k: int) -> int:
        n = len(nums)
        q = deque([(nums[0], 0)])
        res, maxl = nums[0], nums[0]
        for i in range(1, n):
            while i - q[0][1] > k:
                q.popleft()
            res = max(0, q[0][0]) + nums[i]
            maxl = max(maxl, res)
            while q and res >= q[-1][0]:
                q.pop()
            q.append((res, i))
        return maxl

2、 跳跃游戏 VI

class Solution:
    def maxResult(self, nums: List[int], k: int) -> int:
        n = len(nums)
        q = deque([(nums[0], 0)])
        res = nums[0]
        for i in range(1, n):
            while i - q[0][1] > k:
                q.popleft()
            res = q[0][0] + nums[i]
            while q and res >= q[-1][0]:
                q.pop()
            q.append((res, i))
        return res

 参考资料:

单调栈 + 前缀和的前缀和(Python/Java/C++/Go)

单调栈求区间

42. 接雨水 - 接雨水 - 力扣(LeetCode)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
单调栈是一种常用的数据结构,用于解决一类特定的问题,其中最常见的问题是找到数组中每个元素的下一个更大或更小的元素。在Codeforces编程竞赛中,单调栈经常被用于解决一些与数组相关的问题。 下面是单调栈的一般思路: 1. 创建一个空栈。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将当前元素入栈。 4. 重复步骤3,直到遍历完所有元素。 这样,最后剩下的栈中元素就是没有下一个更大或更小元素的元素。在使用单调栈求解具体问题时,我们可以根据需要进行一些特定的操作。 例如,如果要找到一个数组中每个元素的下一个更大的元素,可以使用单调递减栈。具体操作如下: 1. 创建一个空栈和一个空结果数组。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将其在结果数组中的位置记录为当前元素的下一个更大元素的索引。 4. 将当前元素入栈。 5. 重复步骤3和4,直到遍历完所有元素。 6. 结果数组中没有下一个更大元素的位置,可以设置为-1。 以下是一个使用单调递减栈求解下一个更大元素问题的示例代码: ```cpp #include <iostream> #include <stack> #include <vector> std::vector<int> nextGreaterElement(std::vector<int>& nums) { int n = nums.size(); std::vector<int> result(n, -1); std::stack<int> stack; for (int i = 0; i < n; i++) { while (!stack.empty() && nums[i] > nums[stack.top()]) { result[stack.top()] = i; stack.pop(); } stack.push(i); } return result; } int main() { std::vector<int> nums = {1,3, 2, 4, 5, 1}; std::vector<int> result = nextGreaterElement(nums); for (int i = 0; i < result.size(); i++) { std::cout << "Next greater element for " << nums[i] << ": "; if (result[i] != -1) { std::cout << nums[result[i]]; } else { std::cout << "None"; } std::cout << std::endl; } return 0; } ``` 以上代码将输出: ``` Next greater element for 1: 3 Next greater element for 3: 4 Next greater element for 2: 4 Next greater element for 4: 5 Next greater element for 5: None Next greater element for 1: None ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值