数据结构预算法——使用栈和队列的题目

一、柱状图中最大的矩形

此题为leetcode第84题
思路:枚举每一根柱子i的高度hight[i],随后我们需要进行向左右两边扩展,使得扩展到的柱子的高度均不小于h。换句话说,我们需要找到左右两侧最近的高度小于h的柱子,这样这两根柱子之间(不包括其本身)的所有柱子高度均不小于h,并且就是i能够扩展到的最远范围。使用单调栈可以实现这样的功能,即栈中元素都是单调递增或递减的。这里我们使用单调递增的栈。

  • (1)当前位第i个柱子,栈中存放j值,j为i左边的柱子。从栈底到栈顶,j的值严格单调递增,同时对应的高度值也严格单调递增。(2)当我们枚举到第i根柱子时,我们从栈顶不断地移除height[j] > height[i]的j值。在移除完毕后,栈顶的j值就一定满足height[j] < height[i],此时j就是i左侧且最近的小于其高度的柱子。这里会有一种特殊情况。如果我们移除了栈中所有的j值,那就说明i左侧所有柱子的高度都大于height[i],那么我们可以认为i左侧且最近的小于其高度的柱子在位置 j=−1,它是一根「虚拟」的、高度无限低的柱子。这样的定义不会对我们的答案产生任何的影响,我们也称这根「虚拟」的柱子为「哨兵」。(3)我们再将i放入栈顶。
  • 根据以上步骤,我们从左往右遍历得到每个柱子的左边界,再从右往左遍历得到每个柱子的右边界,步骤如下图所示(为了便于理解,图中栈的元素也存放了高度,即(h, j))。然后求出每个柱子对应的最大矩形,返回其中最大的。
    在这里插入图片描述
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        left, right = [0] * n, [0] * n
        
        stack = []
        for i in range(n):
            while stack and heights[i] <= heights[stack[-1]]:
                stack.pop()
            left[i] = stack[-1] if stack else -1
            stack.append(i)
        
        stack = []
        for i in range(n - 1, -1, -1):
            while stack and heights[i] <= heights[stack[-1]]:
                stack.pop()
            right[i] = stack[-1] if stack else n
            stack.append(i)
        
        res = max([(right[i] - left[i] - 1) * heights[i] for i in range(n)]) if n > 0 else 0
        return res

二、最小栈

此题为leetcode第155题
思路:定义一个最小值self.min,在每轮push的时候更新最小值,然后将最小值连通x一起push到栈中,这样在pop的时候直接取栈顶更新最小最即可。

class MinStack:
    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.min = float('inf')

    def push(self, x: int) -> None:
        if self.min > x:
            self.min = x
        self.stack.append((x, self.min))

    def pop(self) -> None:
        self.stack.pop()
        if self.stack:
            self.min = self.getMin()
        else:
            self.min = float('inf')

    def top(self) -> int:
        if self.stack:
            return self.stack[-1][0]

    def getMin(self) -> int:
        if self.stack:
            return self.stack[-1][1]

# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

三、滑动窗口最大值

此题为leetcode第239题
思路:维护一个双端队列q,队列里的数是数组元素的索引。遍历数组,每次循环做三件事情:(1)维护队列的单调性,我们希望队列的最左端是当前窗口的最大值,往右依次递减,因此如果nums[i] > nums[q[-1]]时就要依次弹出队列右边的元素;(2)维护队列的范围,我们希望队列的长度为k,因此当q[0] < i – k + 1时依次弹出队列左边的元素,符合范围要求后将当前的索引入队;(3)当形成窗口时,即i >= k – 1时,队列左端元素即为当前窗口最大元素。

from collections import deque
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        q = deque()
        res = []
        for i in range(n):
            # 维护递减队列
            while q and nums[q[-1]] < nums[i]:
                q.pop()
            # 维护队列范围
            while q and q[0] < i - k + 1:
                q.popleft()
            q.append(i)
            # 形成窗口时添加答案
            if i >= k - 1:
                res.append(nums[q[0]])
        return res

四、函数的独占时间

此题为leetcode第636题
思路:可以用栈来模拟函数的调用。栈里的元素是函数id,当函数如果为start的话,计算一下和前一个函数的时间差,加在前一个函数上,并将当前函数id入栈;如果为end的话,同样计算一下和前一个函数(实际上为同一个函数)的时间差,加在前一个函数上,并将前一个函数出栈。

class Solution:
    def exclusiveTime(self, n: int, logs: List[str]) -> List[int]:
        stack = []
        res = [0] * n
        
        s = logs[0].split(':')
        stack.append(int(s[0]))
        prev = int(s[2])
        for i in range(1, len(logs)):
            s = logs[i].split(':')
            if s[1] == 'start':
                if len(stack) != 0:
                    res[stack[-1]] += int(s[2]) - prev
                stack.append(int(s[0]))
                prev = int(s[2])
            else:
                res[stack[-1]] += int(s[2]) - prev + 1
                stack.pop()
                prev = int(s[2]) + 1
        return res

五、验证栈序列

此题为leetcode第946题
思路:我们用一个辅助栈stack,根据给定的pushed和popped模拟入栈出栈的过程,如果模拟成功的话就符合题意。遍历pushed里的元素,在当前循环里,将元素压入栈中,然后循环出栈(循环出栈的条件是stack不为空且栈顶元素等于popped[i],并且执行i += 1)。因为数组中不包含重复元素,因此每个元素在哪个位置是唯一的,保证了出栈顺序的正确性。最后外层循环结束后,如果stack为空则返回true,否则返回false。

class Solution:
    def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
        n = len(pushed)
        if n <= 1:
            return True
        
        stack = []
        p = 0   # 指向popped
        for num in pushed:
            stack.append(num)
            # 循环出栈
            while stack and stack[-1] == popped[p]:
                stack.pop()
                p += 1
        return not stack

未完待续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值