栈与队列部分算法题(代码随想录)

栈与队列

1.用栈实现队列

2.用队列实现栈

3.有效的括号

def isValid(self, s: str) -> bool:
    stack = []
    for i in s:
        if i == "[":
            stack.append("]")
        elif i == "{":
            stack.append("}")
        elif i == "(":
            stack.append(")")
        elif not stack or stack[-1] != i:
            return False
        else: 
            stack.pop()
    return not stack # 等价于len(stack) == 0 or stack is None

4.删除字符串中的所有相邻重复项

so easy

def removeDuplicates(self, s: str) -> str:
    stack = []
    for i in s:
        if stack and stack[-1] == i:
            stack.pop()
        else:
            stack.append(i)
    return ''.join(stack)

5.逆波兰表达式求值(2)

so easy

def evalRPN(self, tokens: List[str]) -> int:
    stack = []
    ch = {
        '+':lambda a,b:a+b,
        '-':lambda a,b:a-b,
        '*':lambda a,b:a*b,
        '/':lambda a,b:int(a/b),
    }
    for s in tokens:
        if s not in ch.keys():
            stack.append(int(s))
        else:
            b = stack.pop()
            a = stack.pop()
            r = ch[s](a,b)
            stack.append(r)
    return stack.pop()

6.滑动窗口最大值(3)

单调队列核心思想:之前的元素比当前元素还小的话,可以提前移除之前的元素,不需要再维护无用的元素

注意清除已过期的元素

def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
    n = len(nums)
    # from collections import deque 导入
    q = deque()
    # 将前k个元素入队
    for i in range(k):
        while q and nums[i] >= nums[q[-1]]:
            q.pop()
        q.append(i)
    res = [nums[q[0]]]
    for i in range(k,n):
        while q and nums[i] >= nums[q[-1]]:
            q.pop()
        q.append(i)
        if i - q[0] >= k:
            q.popleft()
        res.append(nums[q[0]])
    return res

精简融合版

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[i] >= nums[q[-1]]:
            q.pop()
        # 添加到队列中
        q.append(i)
        # 滑动窗口大小大于k,移除队头元素(一定要在下一步之前)
        # q[0]和q[1]都在队列中,q[0]一定比q[1]早过期,
        # 若q[1]比q[0]大,q[1]入队时,q[0]便会出队
        # 若q[1]比q[0]小,q[1]肯定是后入队的,否则q[0]会把q[1]排除掉
        if i - q[0] >= k:
            q.popleft()
        # 滑动窗口大小为k时,添加结果
        if i + 1 >= k:
            res.append(nums[q[0]])
    return res

7.前 K 个高频元素(2)

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

最大堆:有n个元素,每次维护要logk的复杂度,所以总时间复杂度为nlogk,空间复杂度最差map要存储n个键值对,为O(n)

def topKFrequent(self, nums: List[int], k: int) -> List[int]:
    mp = {}
    for n in nums:
        mp[n] = mp.get(n,0) + 1
    q = []
    for key,value in mp.items():
        heapq.heappush(q,(value,key))
        if len(q) > k:
            heapq.heappop(q)
    res = [0] * k
    for i in range(k - 1,-1,-1):
        res[i] = heapq.heappop(q)[1]
    return res

单调队列

单调栈

在一维数组中对每一个数找到第一个比自己小的元素。这类“在一维数组中找第一个满足某种条件的数”的场景就是典型的单调栈应用场景。The Next Greater问题

1.柱状图中最大的矩形(3)

基本思路:遍历每个矩阵,以这个矩阵为中心(即高度是此矩阵)向左右扩展,找比这个矩阵最近小的元素。

暴力法:O(n^2),leetcode 测试案例87/98

def largestRectangleArea(self, heights: List[int]) -> int:
    # 从左向右遍历:以每一根柱子为主心骨(当前轮最高的参照物),迭代直到找到左侧和右侧各第一个矮一级的柱子
    res = 0
    for i in range(len(heights)):
        left = i
        right = i
        for _ in range(left,-1,-1):
            if heights[left] < heights[i]:
                break
            left -= 1
        for _ in range(right,len(heights)):
            if heights[right] < heights[i]:
                break
            right += 1
        res = max(res,(right-left-1) * heights[i])
    return res

动态规划

用两个dp数组保存左右两边最近比当前小的柱子的下标

def largestRectangleArea(self, heights: List[int]) -> int:
    n = len(heights)
    # 两个DP数列储存的均是下标index
    res = 0
    lIndex = [0] * n
    rIndex = [0] * n
    lIndex[0] = -1
    for i in range(1,n):
        temp = i - 1
        while temp >= 0 and heights[temp] >= heights[i]:
            temp = lIndex[temp]
        lIndex[i] = temp
    rIndex[-1] = n
    for i in range(n-2,-1,-1):
        temp = i + 1
        while temp < n and heights[temp] >= heights[i]:
            temp = rIndex[temp]
        rIndex[i] = temp

    for i in range(n):
        res = max(res,heights[i]*(rIndex[i]-lIndex[i]-1))
    return res

单调栈:在此题中递增,由于单调栈的性质,中心点向右扩展就是while循环进去的时机,向左扩展就是根据当前点到栈顶索引之差

注意:当前点的扩展,可能并不是在遍历此点的周期内,可能是在遍历其他点的时候,走while循环能够从栈中弹出的时候。

tips:为防止左右越界,我们可以在左右两侧虚拟两根无限低的柱子,记高度为 0。

def largestRectangleArea(self, heights: List[int]) -> int:
    q = []
    heights.insert(0,0)
    heights.append(0)
    n = len(heights)
    res = 0
    for i in range(n):
        while q and heights[i] < heights[q[-1]]:
            cur = q.pop()
            left = q[-1]
            right = i - 1
            res = max(res,(right - left)*heights[cur])
        q.append(i)
    return res
2.每日温度(2)

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

def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
    q = []
    n = len(temperatures)
    res = [0] * n
    for i in range(n):
        while q and temperatures[i] > temperatures[q[-1]]:
            pp = q.pop()
            res[pp] = i - pp
        q.append(i)
    return res
3.下一个更大元素 I

典型的单调栈问题

def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
    mp = {}
    q = []
    for i in range(len(nums2)):
        while q and nums2[i] > nums2[q[-1]]:
            index = q.pop()
            mp[nums2[index]] = nums2[i]
        q.append(i)
    res = []
    for num in nums1:
        if num in mp:
           res.append(mp[num])
        else:
            res.append(-1)
    return res
4.下一个更大元素 II(2)

这题元素可以重复,所以往map里面放下标

def nextGreaterElements(self, nums: List[int]) -> List[int]:
    mp = {}
    q = []
    temp = nums.copy()
    for t in temp:
        nums.append(t)
    for i in range(len(nums)):
        while q and nums[i] > nums[q[-1]]:
            index = q.pop()
            mp[index] = nums[i]
        q.append(i)
    res = []
    for i in range(len(temp)):
        if i in mp:
            res.append(mp[i])
        else:
            res.append(-1)
    return res
5.接雨水(3)

双指针

思路:逐个计算每个位置的雨水量,找到该位置左边和右边最高的柱子,由于宽就是1,高是min(rHeight,lHeight)-height[i],就能得出该位置的雨水量

时间复杂度为O(n^2),leetcode通过测试案例 320/322

def trap(self, height: List[int]) -> int:
    sum = 0
    for i in range(len(height)):
        if i == 0 or i == len(height) - 1:
            continue
        lHeight,rHeight = height[i],height[i]
        for l in range(i-1,-1,-1):
            if height[l] > lHeight:
                lHeight = height[l]
        if lHeight == height[i]: continue
        for r in range(i+1,len(height)):
            if height[r] > rHeight:
                rHeight = height[r]
            if rHeight > lHeight:
                continue
        sum = sum + min(rHeight,lHeight) - height[i]
    return sum

动态规划

原来双指针找左右两边的最高柱子,有冗余过程,

我们事先用两个数组保存左右两边的最高柱子的高度。

时间复杂度为O(n)

def trap(self, height: List[int]) -> int:
    n = len(height)
    if n <= 2:
        return 0
    lHeight = [0] * n
    rHeight = [0] * n
    lHeight[0] = height[0]
    for i in range(1,n):
        lHeight[i] = max(height[i],lHeight[i-1])
    rHeight[-1] = height[-1]
    for i in range(n-2,-1,-1):
        rHeight[i] = max(height[i],rHeight[i+1])
    sum = 0
    for i in range(n):
        sum = sum + min(lHeight[i],rHeight[i]) - height[i]
    return sum

单调栈

计算雨水并不是像前两种方法一样-------逐个计算每个位置的雨水量

这里计算出

高度和前两种方法类似h = min(height[i],height[st[-1]]) - height[mid],得出左右两边最高柱子的最小值再减去底部的大小

宽度当前柱子下标减去凹陷部位前一个柱子下标再减1

最后v = h*w

def trap(self, height: List[int]) -> int:
    st = [0]
    sum = 0
    for i in range(1,len(height)):
        while st and height[i] > height[st[-1]]:
            mid = st.pop()
            if st:
                h = min(height[i],height[st[-1]]) - height[mid]
                w = i - st[-1] - 1
                sum += h*w
        st.append(i)
    return sum
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值