Files

143 lines
5.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""面试高频算法模式(只用标准库)。每个函数注明时间 / 空间复杂度;`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("全部自检通过")