返回顶部

算法面试通关 — 堆栈与队列

堆栈、队列(Stack、Queue)

堆栈、队列(Stack、Queue)

1. Stack(堆栈) - 先进先出  只有一个端

  • Array or Linked List

2. Queue(队列) - 先进后出 有两端 一端进一端出

  • Array or Linked List

Stack (堆栈)

 

Queue (队列)

 

常用数据结构操作

 

 大O计数法

 

 面试题

判断一个括号字符串是否有效

给定一个只包括 '(',')','{','}','[',']' 的字符串 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

用队列实现栈

请你仅使用两个队列实现一个后入先出(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())

优先队列 (了解背后的实现机制即可)

PriorityQueue - 优先队列
  • 正常⼊、按照优先级出

2种实现方式

  • 1. Heap (Binary, Binomial, Fibonacci) 队
  • Binary Search Tree  二叉树搜索
Mini Heap
 
Max Heap

 

 面试题

数据流中的第 K 大元素

 

设计一个找到数据流中第 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)

  

滑动窗口最大值

给你一个整数数组 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)

  

 

posted @   Crazymagic  阅读(29)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示

目录导航