本课程由机器自动翻译成中文,尚未经过人工校对。如果读起来有些奇怪,请以英文原文为准。
顶级面试教练不会向你收取 500 美元的费用来学习以下秘密:大约 90% 的编程面试问题仅围绕七种数据结构。如果你深刻理解何时以及为何要接触每一个目标,那么你就已经成功了一半。另一半是练习 - 但如果没有正确的思维模式,练习就只是记忆。本课程为您提供了地图,因此您遇到的每个问题都感觉像是熟悉的领域。
每次技术面试都来自同一口井。以下是您将遇到的结构,按频率排名:
|数据结构|采访频率 |核心运营|当面试官期待时 | |---|---|---|---| | 数组/字符串 | ★★★★★ |访问 O(1)、搜索 O(n)、插入 O(n) |几乎所有问题都从这里开始 | | 哈希映射/集 | ★★★★★ |插入 O(1),查找 O(1),删除 O(1) | “将其从 O(n²) 优化到 O(n)”| | 链接列表 | ★★★★☆ |插入 O(1),删除 O(1),访问 O(n) |指针操作、循环检测| | 堆栈和队列 | ★★★★☆ |推入/弹出 O(1),入队/出队 O(1) |解析、BFS、撤消操作 | | 树(二进制,BST) | ★★★★☆ |搜索 O(log n),插入 O(log n) |分层数据、排序操作 | | 图表 | ★★★☆☆ |取决于代表性|关系、路径、连通性| | 堆 | ★★★☆☆ |插入 O(log n),提取最小 O(log n) | “找到 k 个最大/最小”|
数组是几乎每个面试问题的起点。关键的见解是,大多数数组问题实际上与数组无关 - 它们与识别两个指针、滑动窗口或前缀和等模式有关。
# Two Sum — the most classic interview problem
# "Given an array and target, find two numbers that add to target"
def two_sum(nums, target):
seen = {} # Hash map to store complement
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
# Time: O(n), Space: O(n) — trading space for time
请注意,即使这个“数组”问题实际上也是一个哈希映射问题。这种模式不断重复:暴力破解在数组上使用嵌套循环,而最佳解决方案引入了哈希映射。
如果您遇到任何问题并且暴力破解的复杂度为 O(n²),请问问自己:“哈希映射可以将其简化为 O(n) 吗?”答案往往是肯定的。
当您需要时,哈希映射会发挥作用:
// Group Anagrams — a classic hash map problem
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (String s : strs) {
char[] chars = s.toCharArray();
Arrays.sort(chars);
String key = new String(chars);
map.computeIfAbsent(key, k -> new ArrayList<>()).add(s);
}
return new ArrayList<>(map.values());
}
链表问题测试您在不丢失节点的情况下操作指针的能力。您需要的三种技术是:
登录 后参与讨论
# Detect a cycle in a linked list (Floyd's Algorithm)
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
堆栈 (LIFO) 和队列 (FIFO) 是更简单的结构,但它们可以解决整个类别的问题:
# Valid Parentheses — classic stack problem
def is_valid(s: str) -> bool:
stack = []
matching = {')': '(', '}': '{', ']': '['}
for char in s:
if char in matching:
if not stack or stack[-1] != matching[char]:
return False
stack.pop()
else:
stack.append(char)
return len(stack) == 0
树问题通常涉及递归和三种遍历顺序之一(中序、前序、后序)。需要开发的思维模型是:“我需要左子树、右子树什么,以及如何在当前节点将它们组合起来?”
# Maximum depth of a binary tree — the template for tree recursion
def max_depth(root):
if not root:
return 0
left_depth = max_depth(root.left)
right_depth = max_depth(root.right)
return 1 + max(left_depth, right_depth)
BST(二叉搜索树)问题添加了一个重要属性:有序遍历产生排序元素。如果您在同一个问题中看到“排序”和“树”,请立即考虑 BST。
图问题令人生畏,但一旦构建了邻接表,大多数问题都可以简化为 BFS 或 DFS。关键技能是:
# BFS shortest path in an unweighted graph
from collections import deque
def shortest_path(graph, start, end):
queue = deque([(start, 0)])
visited = {start}
while queue:
node, distance = queue.popleft()
if node == end:
return distance
for neighbour in graph[node]:
if neighbour not in visited:
visited.add(neighbour)
queue.append((neighbour, distance + 1))
return -1 # Not reachable
每当一个问题说“找到 K 个最大的”、“K 个最接近的”或“合并 K 个排序列表”时,堆(优先级队列)几乎肯定是答案。在Python中,heapq提供了一个最小堆;对于最大堆,对值求负。
import heapq
# Find the K largest elements in an array
def k_largest(nums, k):
return heapq.nlargest(k, nums) # Uses a min-heap of size k internally
每次插入时,您需要在 O(log n) 内找到数字流的中位数。哪种数据结构组合最合适?
|结构|访问 |搜索 |插入 |删除 |空间| |---|---|---|---|---|---| |数组| O(1) | O(1) | O(n) | O(n) | O(n) | O(n) | |哈希映射 | - | O(1)* | O(1)* O(1)* | O(1)* O(1)* | O(1)* O(n) | |链接列表 | O(n) | O(n) | O(1) | O(1) | O(1) | O(1) | O(n) | |堆栈/队列| O(n) | O(n) | O(1) | O(1) | O(1) | O(1) | O(n) | | BST(平衡)| O(log n) | O(log n) | O(log n) | O(log n) | O(n) | |堆| - | O(n) | O(log n) | O(log n) | O(n) |
* 平均情况;由于哈希冲突,最坏的情况是 O(n)。
在阅读问题时制定以下心理清单: