基础数据结构

刷题之树、链表、栈、堆

Posted by BX on Thu, May 1, 2025

RoadMap

  • 题型分类与专属模版

    • 数据结构题(链表、树、栈、队列、图)
    • 双指针与滑动窗口
    • 二分查找
    • 动态规划
    • BFS/DFS
    • 回溯/状态压缩 * 贪心算法 * 数学与位运算
    • 堆与优先队列
    • 并查集

🌳 一、树(Tree)相关题型与模板

🧩 核心概念:

  • 树是一个无环、连通的图结构
  • 二叉树是最常见的树类型
  • 遍历方式:前序、中序、后序、层序
  • 应用:递归、DFS、分治、BST 特性

🔍 常见题型:

1. 二叉树的最大深度(LeetCode 104)

给定一个二叉树,找出其最大深度。

1def maxDepth(root):
2    if not root:
3        return 0
4    return 1 + max(maxDepth(root.left), maxDepth(root.right))

✅ 技巧:递归地计算左右子树深度,取较大值 +1。

2. 从前序与中序遍历序列构造二叉树(LeetCode 105)

给定前序和中序遍历构造唯一的二叉树。

1def buildTree(preorder, inorder):
2    if not preorder:
3        return None
4    root = TreeNode(preorder[0])
5    idx = inorder.index(preorder[0])
6    root.left = buildTree(preorder[1:1+idx], inorder[:idx])
7    root.right = buildTree(preorder[1+idx:], inorder[idx+1:])
8    return root

✅ 技巧:前序首位是根节点,中序将其分为左右子树。

3. 二叉搜索树的最近公共祖先(LeetCode 235)

给定一棵 BST,找出两个节点的最近公共祖先。

1def lowestCommonAncestor(root, p, q):
2    if p.val < root.val and q.val < root.val:
3        return lowestCommonAncestor(root.left, p, q)
4    if p.val > root.val and q.val > root.val:
5        return lowestCommonAncestor(root.right, p, q)
6    return root

✅ 技巧:利用 BST 性质,往左或往右找。

🔗 二、链表(Linked List)相关题型与模板

🧩 核心概念:

  • 单向链表/双向链表,重要的是节点引用操作
  • 常用技巧:快慢指针、虚拟头节点、反转

🔍 常见题型:

1. 反转链表(LeetCode 206)

将单向链表反转

1def reverseList(head):
2    prev = None
3    curr = head
4    while curr:
5        tmp = curr.next
6        curr.next = prev
7        prev = curr
8        curr = tmp
9    return prev

✅ 技巧:迭代中不断调整当前节点指向。

2. 环形链表(LeetCode 141)

判断链表中是否有环

1def hasCycle(head):
2    slow = fast = head
3    while fast and fast.next:
4        slow = slow.next
5        fast = fast.next.next
6        if slow == fast:
7            return True
8    return False

✅ 技巧:快慢指针相遇说明有环。

3. 合并两个有序链表(LeetCode 21)

合并两个升序链表为一个新的升序链表

 1def mergeTwoLists(l1, l2):
 2    dummy = ListNode(0)
 3    curr = dummy
 4    while l1 and l2:
 5        if l1.val < l2.val:
 6            curr.next = l1
 7            l1 = l1.next
 8        else:
 9            curr.next = l2
10            l2 = l2.next
11        curr = curr.next
12    curr.next = l1 or l2
13    return dummy.next

✅ 技巧:归并思想,构造 dummy 节点避免边界问题。

📚 三、栈(Stack)相关题型与模板

🧩 核心概念:

  • LIFO(后进先出)结构
  • 常用于:括号匹配、单调栈、前缀中缀后缀表达式

🔍 常见题型:

1. 有效的括号(LeetCode 20)

判断字符串中括号是否成对匹配。

 1def isValid(s):
 2    stack = []
 3    mapping = {')': '(', ']': '[', '}': '{'}
 4    for c in s:
 5        if c in mapping:
 6            if not stack or stack.pop() != mapping[c]:
 7                return False
 8        else:
 9            stack.append(c)
10    return not stack

✅ 技巧:遇到右括号出栈匹配,最后栈需为空。

2. 每日温度(LeetCode 739)

给定每日气温,返回几天后会升温。

1def dailyTemperatures(temperatures):
2    res = [0] * len(temperatures)
3    stack = []
4    for i, temp in enumerate(temperatures):
5        while stack and temp > temperatures[stack[-1]]:
6            idx = stack.pop()
7            res[idx] = i - idx
8        stack.append(i)
9    return res

✅ 技巧:单调递减栈,栈里放的是下标。

3. 最小栈(LeetCode 155)

实现一个支持获取最小值的栈

 1class MinStack:
 2    def __init__(self):
 3        self.stack = []
 4        self.min_stack = []
 5
 6    def push(self, val):
 7        self.stack.append(val)
 8        if not self.min_stack or val <= self.min_stack[-1]:
 9            self.min_stack.append(val)
10
11    def pop(self):
12        if self.stack.pop() == self.min_stack[-1]:
13            self.min_stack.pop()
14
15    def top(self):
16        return self.stack[-1]
17
18    def getMin(self):
19        return self.min_stack[-1]

✅ 技巧:辅助栈存当前最小值。

📥 四、队列(Queue)相关题型与模板

🧩 核心概念:

  • FIFO(先进先出)结构
  • 常用于:BFS、滑动窗口、单调队列等

🔍 常见题型:

1. 滑动窗口最大值(LeetCode 239)

给定一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到最右侧,返回窗口中的最大值。

 1# 单调队列:队头始终是窗口内最大值的索引
 2from collections import deque
 3
 4def maxSlidingWindow(nums, k):
 5    q = deque()
 6    res = []
 7    for i in range(len(nums)):
 8        while q and nums[q[-1]] < nums[i]:
 9            q.pop()
10        q.append(i)
11        if q[0] <= i - k:
12            q.popleft()
13        if i >= k - 1:
14            res.append(nums[q[0]])
15    return res

✅ 技巧:用双端队列维护一个单调递减队列(下标),保证窗口最大值总在队首。

2. 打开转盘锁(LeetCode 752)

给定一个初始为 “0000” 的锁,通过旋转任意一位可以变成邻近数字,返回最少多少步能到达目标组合。

 1# BFS 最短路径
 2from collections import deque
 3
 4def openLock(deadends, target):
 5    dead = set(deadends)
 6    visited = set('0000')
 7    q = deque([('0000', 0)])
 8    while q:
 9        node, step = q.popleft()
10        if node in dead:
11            continue
12        if node == target:
13            return step
14        for i in range(4):
15            for d in (-1, 1):
16                n = node[:i] + str((int(node[i]) + d) % 10) + node[i+1:]
17                if n not in visited:
18                    visited.add(n)
19                    q.append((n, step + 1))
20    return -1

✅ 技巧:标准 BFS 模板,状态压缩可视为图搜索,防止重复访问用 visited 集合。

🏔️ 五、堆(Heap)相关题型与模板

🧩 核心概念:

  • 最小堆 / 最大堆:可快速获取最小(或最大)元素
  • Python 中使用 heapq 默认是最小堆
  • 常用于:Top K 问题、合并多个有序流、优先队列

🔍 常见题型:

1. 数组中的第 K 个最大元素(LeetCode 215)

给定一个无序数组,找出其中第 k 个最大的元素。

1import heapq
2
3def findKthLargest(nums, k):
4    return heapq.nlargest(k, nums)[-1]  # 或者手动维护一个最小堆

✅ 技巧:使用 heapq.nlargest(k, nums) 直接返回前 K 大元素列表。

2. 合并 K 个升序链表(LeetCode 23)

将 k 个升序链表合并为一个升序链表,返回合并后的链表。

 1import heapq
 2
 3class Solution:
 4    def mergeKLists(self, lists):
 5        heap = []
 6        for i, l in enumerate(lists):
 7            if l:
 8                heapq.heappush(heap, (l.val, i, l))
 9
10        dummy = ListNode(0)
11        curr = dummy
12        while heap:
13            val, i, node = heapq.heappop(heap)
14            curr.next = node
15            curr = curr.next
16            if node.next:
17                heapq.heappush(heap, (node.next.val, i, node.next))
18        return dummy.next

✅ 技巧:堆中维护每个链表当前节点,利用 Python 最小堆自动排序。加上索引避免值相等时报错。

3. 前 K 个高频元素(LeetCode 347)

给定一个非空数组,返回出现频率前 k 高的元素。

1from collections import Counter
2import heapq
3
4def topKFrequent(nums, k):
5    counter = Counter(nums)
6    return [item for item, freq in heapq.nlargest(k, counter.items(), key=lambda x: x[1])]

✅ 技巧:Counter 统计频率 + heapq.nlargest 快速找出 Top K。