算法面试通关 — 堆栈与队列
堆栈、队列(Stack、Queue)
堆栈、队列(Stack、Queue)
1. Stack(堆栈) - 先进先出 只有一个端
- Array or Linked List
2. Queue(队列) - 先进后出 有两端 一端进一端出
- Array or Linked List
Stack (堆栈)
常用数据结构操作
大O计数法
面试题
1 判断一个括号字符串是否有效
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
https://leetcode.cn/problems/check-if-a-parentheses-string-can-be-valid/
左括号入栈,右括号出栈,最后判断栈是否为空
1 2 3 4 5 6 7 8 9 | def isValid(s): stack = [] paren_map = { ')' : '(' , ']' : '[' , '}' : '{' } for c in s: if c not in paren_map: stack.append(c) elif not stack or paren_map[c] ! = stack.pop(): return False return not stack |
2 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
注意:
你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | class MyQueue( object ): def __init__( self ): """ Initialize your data structure here. """ self .input_stack = [] self .output_stack = [] def push( self , x): """ Push element x to the back of queue. :type x: int :rtype: None """ self .input_stack.append(x) def pop( self ): """ Removes the element from in front of queue and returns that element. :rtype: int """ if self .empty(): return None else : if len ( self .output_stack): return self .output_stack.pop() else : # for i in xrange(0, len(self.input_stack)): # self.output_stack.append(self.input_stack.pop()) length = len ( self .input_stack) self .output_stack = map ( lambda x: self .input_stack.pop(), range ( 0 , length)) return self .output_stack.pop() def peek( self ): """ Get the front element. :rtype: int """ if self .empty(): return None else : if len ( self .output_stack): return self .output_stack[ - 1 ] else : # for i in xrange(0, len(self.input_stack)): # self.output_stack.append(self.input_stack.pop()) length = len ( self .input_stack) self .output_stack = map ( lambda x: self .input_stack.pop(), range ( 0 , length)) return self .output_stack[ - 1 ] def empty( self ): """ Returns whether the queue is empty. :rtype: bool """ return bool ( len ( self .input_stack) = = 0 and len ( self .output_stack) = = 0 ) |
3 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
说明:
你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class MyQueue: def __init__( self ): self . list = [] # 进队 def push( self , x: int ) - > None : self . list .append(x) # 返回队首元素并出队 def pop( self ) - > int : x = self . list [ 0 ] self . list = self . list [ 1 :] return x # 返回队首元素 def peek( self ) - > int : return self . list [ 0 ] # 判断队列是否为空 def empty( self ) - > bool : if len ( self . list ) = = 0 : return True else : return False if __name__ = = "__main__" : obj = MyQueue() obj.push( 1 ) obj.push( 2 ) print (obj.peek()) print (obj.pop()) print (obj.empty()) |
优先队列 (了解背后的实现机制即可)
- 正常⼊、按照优先级出
2种实现方式
- 1. Heap (Binary, Binomial, Fibonacci) 队
-
Binary Search Tree 二叉树搜索


面试题
设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。
解题思路: 使用最小堆来实现, 堆的长度就是k
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import heapq class Solution( object ): @staticmethod def findKthLargest(nums, k): """ :type nums: List[int] :type k: int :rtype: int """ heap = [] heapq.heapify(heap) for n in nums: if len (heap) < k: heapq.heappush(heap, n) elif n > heap[ 0 ]: heapq.heapreplace(heap, n) return heap[ 0 ] if __name__ = = '__main__' : res = Solution.findKthLargest([ 1 , 6 , 8 , 9 ], 3 ) print (res) |
2 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
题解:双端队列实现 比较的都是下标,基于下标来维护滑动窗口
首先窗口向右滑动的过程就是将窗口最左侧的元素删除,同时在窗口的最右侧添加一个新的元素,这就要用到双端队列,然后找双端队列中的最大元素。
那剩下就是如何找到滑动窗口中的最大值。
那我们就可以只在队列中保留可能成为窗口最大元素的元素,去掉不可能成为窗口中最大元素的元素。
想象一下,如果要进来的是个值大的元素,那一定会比之前早进去的值小的元素晚离开队列,而且值大的元素在,都没值小的元素啥事,所以值小的元素直接弹出队列即可。
这样队列里其实维护的一个单调递减的单调队列。
题解:https://leetcode.cn/problems/sliding-window-maximum/solutions/1212012/acm-xuan-shou-tu-jie-leetcode-hua-dong-c-i3wj/?q=%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC&orderBy=most_relevant&languageTags=python3
res 队列存下标 deque 队列存具体值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class Solution: @staticmethod def maxSlidingWindow(nums, k: int ): # nums 数组 k 窗口的大小 # 如果数组为空或 k = 0,直接返回空 if not nums or not k: return [] # 如果数组只有1个元素,直接返回该元素 if len (nums) = = 1 : return [nums[ 0 ]] # 初始化队列和结果,队列存储数组的下标 queue = [] res = [] for i in range ( len (nums)): # 如果当前队列最左侧存储的下标等于 i-k 的值,代表目前队列已满。 # 但是新元素需要进来,所以列表最左侧的下标出队列 if queue and queue[ 0 ] = = i - k: queue.pop( 0 ) # 对于新进入的元素,如果队列前面的数比它小,那么前面的都出队列 while queue and nums[queue[ - 1 ]] < nums[i]: queue.pop() # 新元素入队列 queue.append(i) # 当前的大值加入到结果数组中 if i > = k - 1 : res.append(nums[queue[ 0 ]]) return res if __name__ = = '__main__' : res = Solution.maxSlidingWindow([ 1 , 3 , - 1 , - 3 , 5 , 3 , 6 , 7 ], 2 ) print (res) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理