"""面试高频算法模式(只用标准库)。每个函数注明时间 / 空间复杂度;`python patterns.py` 运行自检,test_patterns.py 是测试。""" from __future__ import annotations import heapq from collections import Counter, OrderedDict, deque from typing import Hashable, Iterable # 1. 哈希表:两数之和 O(n) / O(n) def two_sum(nums: list[int], target: int) -> tuple[int, int] | None: seen: dict[int, int] = {} for i, x in enumerate(nums): if target - x in seen: return seen[target - x], i seen[x] = i return None # 2. 滑动窗口:无重复字符的最长子串 O(n) / O(字符集) def longest_unique_substring(s: str) -> int: last: dict[str, int] = {} left = best = 0 for right, ch in enumerate(s): if last.get(ch, -1) >= left: left = last[ch] + 1 # 窗口左边界跳到重复字符的下一位 last[ch] = right best = max(best, right - left + 1) return best # 3. 二分查找:第一个 >= target 的位置(lower_bound) O(log n) / O(1) def lower_bound(arr: list[int], target: int) -> int: lo, hi = 0, len(arr) # 左闭右开区间 [lo, hi) while lo < hi: mid = (lo + hi) // 2 if arr[mid] < target: lo = mid + 1 else: hi = mid return lo # 4. 堆:出现次数最多的 K 个(例如热门问题) O(n log k) / O(n) def top_k_frequent(items: Iterable[Hashable], k: int) -> list[Hashable]: counts = Counter(items) return [x for _, x in heapq.nlargest(k, ((c, x) for x, c in counts.items()))] # 5. BFS:网格最短路径(0 可走,1 障碍) O(行×列) / O(行×列) def shortest_path(grid: list[list[int]], start: tuple[int, int], end: tuple[int, int]) -> int: rows, cols = len(grid), len(grid[0]) q = deque([(start, 0)]) seen = {start} while q: (r, c), d = q.popleft() if (r, c) == end: return d for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): nr, nc = r + dr, c + dc if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 0 and (nr, nc) not in seen: seen.add((nr, nc)) q.append(((nr, nc), d + 1)) return -1 # 6. DFS + 拓扑排序:任务依赖的执行顺序(有环返回 None) O(V+E) def topo_order(deps: dict[str, list[str]]) -> list[str] | None: """deps[a] = [b, c] 表示 a 依赖 b 和 c(b、c 要先执行)。""" state: dict[str, int] = {} # 0/缺省 未访问,1 访问中,2 已完成 order: list[str] = [] def visit(n: str) -> bool: if state.get(n) == 1: return False # 回到“访问中”的节点:有环 if state.get(n) == 2: return True state[n] = 1 for m in deps.get(n, []): if not visit(m): return False state[n] = 2 order.append(n) return True nodes = set(deps) | {m for ms in deps.values() for m in ms} for n in sorted(nodes): if not visit(n): return None return order # 7. 动态规划:编辑距离(文本相似度、纠错) O(mn) / O(n) def edit_distance(a: str, b: str) -> int: prev = list(range(len(b) + 1)) for i in range(1, len(a) + 1): cur = [i] + [0] * len(b) for j in range(1, len(b) + 1): cur[j] = prev[j - 1] if a[i - 1] == b[j - 1] else 1 + min(prev[j - 1], prev[j], cur[j - 1]) prev = cur return prev[-1] # 8. 设计题:LRU 缓存 get/put 均为 O(1) class LRUCache: def __init__(self, capacity: int): self.capacity = capacity self.data: OrderedDict[Hashable, object] = OrderedDict() def get(self, key: Hashable) -> object | None: if key not in self.data: return None self.data.move_to_end(key) # 最近使用的移到末尾 return self.data[key] def put(self, key: Hashable, value: object) -> None: self.data[key] = value self.data.move_to_end(key) if len(self.data) > self.capacity: self.data.popitem(last=False) # 淘汰最久未使用的(头部) # 9. 区间合并:合并重叠的时间段(排班、会议室) O(n log n) def merge_intervals(intervals: list[tuple[int, int]]) -> list[tuple[int, int]]: out: list[tuple[int, int]] = [] for s, e in sorted(intervals): if out and s <= out[-1][1]: out[-1] = (out[-1][0], max(out[-1][1], e)) else: out.append((s, e)) return out if __name__ == "__main__": assert two_sum([2, 7, 11, 15], 9) == (0, 1) assert longest_unique_substring("abcabcbb") == 3 assert lower_bound([1, 3, 3, 5], 3) == 1 assert top_k_frequent(["退货", "发票", "退货", "物流", "退货", "物流"], 2) == ["退货", "物流"] assert shortest_path([[0, 0, 0], [1, 1, 0], [0, 0, 0]], (0, 0), (2, 0)) == 6 assert topo_order({"deploy": ["test"], "test": ["build"]}) == ["build", "test", "deploy"] assert edit_distance("kitten", "sitting") == 3 assert merge_intervals([(1, 3), (2, 6), (8, 10)]) == [(1, 6), (8, 10)] print("全部自检通过")