← 返回首页
algorithm-pattern-python/data_structure/stack_queue.md at master · dashidhy/algorithm-pattern-python · GitHub
Skip to content

Navigation Menu

Toggle navigation
Sign in
Appearance settings
Search or jump to...

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Resetting focus

Latest commit

 

History

History
596 lines (438 loc) · 17.5 KB
 master
Top

File metadata and controls

  • Preview
  • Code
  • Blame
596 lines (438 loc) · 17.5 KB

栈和队列

简介

栈的特点是后入先出

根据这个特点可以临时保存一些数据,之后用到依次再弹出来,常用于 DFS 深度搜索

队列一般常用于 BFS 广度搜索,类似一层一层的搜索

Stack 栈

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

  • 思路:用两个栈实现或插入元组实现,保证当前最小值在栈顶即可
class MinStack: def __init__(self): self.stack = [] def push(self, x: int) -> None: if len(self.stack) > 0: self.stack.append((x, min(x, self.stack[-1][1]))) else: self.stack.append((x, x)) def pop(self) -> int: return self.stack.pop()[0] def top(self) -> int: return self.stack[-1][0] def getMin(self) -> int: return self.stack[-1][1]

波兰表达式计算 > 输入: ["2", "1", "+", "3", "*"] > 输出: 9 解释: ((2 + 1) * 3) = 9

  • 思路:通过栈保存原来的元素,遇到表达式弹出运算,再推入结果,重复这个过程
class Solution: def evalRPN(self, tokens: List[str]) -> int: def comp(or1, op, or2): if op == '+': return or1 + or2 if op == '-': return or1 - or2 if op == '*': return or1 * or2 if op == '/': abs_result = abs(or1) // abs(or2) return abs_result if or1 * or2 > 0 else -abs_result stack = [] for token in tokens: if token in ['+', '-', '*', '/']: or2 = stack.pop() or1 = stack.pop() stack.append(comp(or1, token, or2)) else: stack.append(int(token)) return stack[0]

给定一个经过编码的字符串,返回它解码后的字符串。 s = "3[a]2[bc]", 返回 "aaabcbc". s = "3[a2[c]]", 返回 "accaccacc". s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".

  • 思路:通过两个栈进行操作,一个用于存数,另一个用来存字符串
class Solution: def decodeString(self, s: str) -> str: stack_str = [''] stack_num = [] num = 0 for c in s: if c >= '0' and c <= '9': num = num * 10 + int(c) elif c == '[': stack_num.append(num) stack_str.append('') num = 0 elif c == ']': cur_str = stack_str.pop() stack_str[-1] += cur_str * stack_num.pop() else: stack_str[-1] += c return stack_str[0]

给定一个二叉树,返回它的中序遍历。

class Solution: def inorderTraversal(self, root: TreeNode) -> List[int]: stack, inorder = [], [] node = root while len(stack) > 0 or node is not None: if node is not None: stack.append(node) node = node.left else: node = stack.pop() inorder.append(node.val) node = node.right return inorder

给你无向连通图中一个节点的引用,请你返回该图的深拷贝(克隆)。

  • BFS
class Solution: def cloneGraph(self, start: 'Node') -> 'Node': if start is None: return None visited = {start: Node(start.val, [])} bfs = collections.deque([start]) while len(bfs) > 0: curr = bfs.popleft() curr_copy = visited[curr] for n in curr.neighbors: if n not in visited: visited[n] = Node(n.val, []) bfs.append(n) curr_copy.neighbors.append(visited[n]) return visited[start]
  • DFS iterative
class Solution: def cloneGraph(self, start: 'Node') -> 'Node': if start is None: return None if not start.neighbors: return Node(start.val) visited = {start: Node(start.val, [])} dfs = [start] while len(dfs) > 0: peek = dfs[-1] peek_copy = visited[peek] if len(peek_copy.neighbors) == 0: for n in peek.neighbors: if n not in visited: visited[n] = Node(n.val, []) dfs.append(n) peek_copy.neighbors.append(visited[n]) else: dfs.pop() return visited[start]

给定一个由  '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

High-level problem: number of connected component of graph

  • 思路:通过深度搜索遍历可能性(注意标记已访问元素)
class Solution: def numIslands(self, grid: List[List[str]]) -> int: if not grid or not grid[0]: return 0 m, n = len(grid), len(grid[0]) def dfs_iter(i, j): dfs = [] dfs.append((i, j)) while len(dfs) > 0: i, j = dfs.pop() if grid[i][j] == '1': grid[i][j] = '0' if i - 1 >= 0: dfs.append((i - 1, j)) if j - 1 >= 0: dfs.append((i, j - 1)) if i + 1 < m: dfs.append((i + 1, j)) if j + 1 < n: dfs.append((i, j + 1)) return num_island = 0 for i in range(m): for j in range(n): if grid[i][j] == '1': num_island += 1 dfs_iter(i, j) return num_island

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。

  • 思路 1:蛮力法,比较每个以 i 开始 j 结束的最大矩形,A(i, j) = (j - i + 1) * min_height(i, j),时间复杂度 O(n^2) 无法 AC。
class Solution: def largestRectangleArea(self, heights: List[int]) -> int: max_area = 0 n = len(heights) for i in range(n): min_height = heights[i] for j in range(i, n): min_height = min(min_height, heights[j]) max_area = max(max_area, min_height * (j - i + 1)) return max_area
  • 思路 2: 设 A(i, j) 为区间 [i, j) 内最大矩形的面积,k 为 [i, j) 内最矮 bar 的坐标,则 A(i, j) = max((j - i) * heights[k], A(i, k), A(k+1, j)), 使用分治法进行求解。时间复杂度 O(nlogn),其中使用简单遍历求最小值无法 AC (最坏情况退化到 O(n^2)),使用线段树优化后勉强 AC。
class Solution: def largestRectangleArea(self, heights: List[int]) -> int: n = len(heights) seg_tree = [None] * n seg_tree.extend(list(zip(heights, range(n)))) for i in range(n - 1, 0, -1): seg_tree[i] = min(seg_tree[2 * i], seg_tree[2 * i + 1], key=lambda x: x[0]) def _min(i, j): min_ = (heights[i], i) i += n j += n while i < j: if i % 2 == 1: min_ = min(min_, seg_tree[i], key=lambda x: x[0]) i += 1 if j % 2 == 1: j -= 1 min_ = min(min_, seg_tree[j], key=lambda x: x[0]) i //= 2 j //= 2 return min_ def LRA(i, j): if i == j: return 0 min_k, k = _min(i, j) return max(min_k * (j - i), LRA(k + 1, j), LRA(i, k)) return LRA(0, n)
  • 思路 3:包含当前 bar 最大矩形的边界为左边第一个高度小于当前高度的 bar 和右边第一个高度小于当前高度的 bar。
class Solution: def largestRectangleArea(self, heights: List[int]) -> int: n = len(heights) stack = [-1] max_area = 0 for i in range(n): while len(stack) > 1 and heights[stack[-1]] > heights[i]: h = stack.pop() max_area = max(max_area, heights[h] * (i - stack[-1] - 1)) stack.append(i) while len(stack) > 1: h = stack.pop() max_area = max(max_area, heights[h] * (n - stack[-1] - 1)) return max_area

Queue 队列

常用于 BFS 宽度优先搜索

使用栈实现队列

class MyQueue: def __init__(self): self.cache = [] self.out = [] def push(self, x: int) -> None: """ Push element x to the back of queue. """ self.cache.append(x) def pop(self) -> int: """ Removes the element from in front of queue and returns that element. """ if len(self.out) == 0: while len(self.cache) > 0: self.out.append(self.cache.pop()) return self.out.pop() def peek(self) -> int: """ Get the front element. """ if len(self.out) > 0: return self.out[-1] else: return self.cache[0] def empty(self) -> bool: """ Returns whether the queue is empty. """ return len(self.cache) == 0 and len(self.out) == 0

二叉树的层序遍历

class Solution: def levelOrder(self, root: TreeNode) -> List[List[int]]: levels = [] if root is None: return levels bfs = collections.deque([root]) while len(bfs) > 0: levels.append([]) level_size = len(bfs) for _ in range(level_size): node = bfs.popleft() levels[-1].append(node.val) if node.left is not None: bfs.append(node.left) if node.right is not None: bfs.append(node.right) return levels

给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。 两个相邻元素间的距离为 1

  • 思路 1: 从 0 开始 BFS, 遇到距离最小值需要更新的则更新后重新入队更新后续结点
class Solution: def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]: if len(matrix) == 0 or len(matrix[0]) == 0: return matrix m, n = len(matrix), len(matrix[0]) dist = [[float('inf')] * n for _ in range(m)] bfs = collections.deque([]) for i in range(m): for j in range(n): if matrix[i][j] == 0: dist[i][j] = 0 bfs.append((i, j)) neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)] while len(bfs) > 0: i, j = bfs.popleft() for dn_i, dn_j in neighbors: n_i, n_j = i + dn_i, j + dn_j if n_i >= 0 and n_i < m and n_j >= 0 and n_j < n: if dist[n_i][n_j] > dist[i][j] + 1: dist[n_i][n_j] = dist[i][j] + 1 bfs.append((n_i, n_j)) return dist
  • 思路 2: 2-pass DP,dist(i, j) = max{dist(i - 1, j), dist(i + 1, j), dist(i, j - 1), dist(i, j + 1)} + 1
class Solution: def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]: if len(matrix) == 0 or len(matrix[0]) == 0: return matrix m, n = len(matrix), len(matrix[0]) dist = [[float('inf')] * n for _ in range(m)] for i in range(m): for j in range(n): if matrix[i][j] == 1: if i - 1 >= 0: dist[i][j] = min(dist[i - 1][j] + 1, dist[i][j]) if j - 1 >= 0: dist[i][j] = min(dist[i][j - 1] + 1, dist[i][j]) else: dist[i][j] = 0 for i in range(-1, -m - 1, -1): for j in range(-1, -n - 1, -1): if matrix[i][j] == 1: if i + 1 < 0: dist[i][j] = min(dist[i + 1][j] + 1, dist[i][j]) if j + 1 < 0: dist[i][j] = min(dist[i][j + 1] + 1, dist[i][j]) return dist

补充:单调栈

顾名思义,单调栈即是栈中元素有单调性的栈,典型应用为用线性的时间复杂度找左右两侧第一个大于/小于当前元素的位置。

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

补充:单调队列

单调栈的拓展,可以从数组头 pop 出旧元素,典型应用是以线性时间获得区间最大/最小值。

求滑动窗口中的最大元素

class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: N = len(nums) if N * k == 0: return [] if k == 1: return nums[:] # define a max queue maxQ = collections.deque() result = [] for i in range(N): if maxQ and maxQ[0] == i - k: maxQ.popleft() while maxQ and nums[maxQ[-1]] < nums[i]: maxQ.pop() maxQ.append(i) if i >= k - 1: result.append(nums[maxQ[0]]) return result
class Solution: def shortestSubarray(self, A: List[int], K: int) -> int: N = len(A) cdf = [0] for num in A: cdf.append(cdf[-1] + num) result = N + 1 minQ = collections.deque() for i, csum in enumerate(cdf): while minQ and csum <= cdf[minQ[-1]]: minQ.pop() while minQ and csum - cdf[minQ[0]] >= K: result = min(result, i - minQ.popleft()) minQ.append(i) return result if result < N + 1 else -1

总结

  • 熟悉栈的使用场景
    • 后入先出,保存临时值
    • 利用栈 DFS 深度搜索
  • 熟悉队列的使用场景
    • 利用队列 BFS 广度搜索

练习

Footer

© 2026 GitHub, Inc.