# 对撞指针模板 left, right = 0, len(arr) - 1 while left < right: if condition(arr[left], arr[right]): # 记录/处理 left += 1 right -= 1 elif too_small: left += 1 else: right -= 1
# 快慢指针模板(原地去重) slow = 0 for fast inrange(len(arr)): if arr[fast] != arr[slow]: slow += 1 arr[slow] = arr[fast]
// 对撞指针模板 left, right := 0, len(arr)-1 for left < right { if condition(arr[left], arr[right]) { // 记录/处理 left++ right-- } elseif tooSmall { left++ } else { right-- } }
// 快慢指针模板(原地去重) slow := 0 for fast := 0; fast < len(arr); fast++ { if arr[fast] != arr[slow] { slow++ arr[slow] = arr[fast] } }
2. 滑动窗口 (Sliding Window)
适用场景:子数组/子串问题,求满足条件的最长/最短窗口。
变长窗口:右指针扩张,不满足时左指针收缩。
定长窗口:窗口大小固定为 k,整体右移。
1 2 3 4 5 6 7 8 9 10 11
# 变长窗口模板(最长满足条件子串) left = 0 window = {} # 或用计数器 for right inrange(len(s)): # 扩张:加入 s[right] window[s[right]] = window.get(s[right], 0) + 1 # 收缩:窗口不合法时移动 left whilenot valid(window): window[s[left]] -= 1 left += 1 ans = max(ans, right - left + 1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 变长窗口模板(最长满足条件子串) window := map[byte]int{} left, ans := 0, 0 for right := 0; right < len(s); right++ { // 扩张:加入 s[right] window[s[right]]++ // 收缩:窗口不合法时移动 left for !valid(window) { window[s[left]]-- left++ } if right-left+1 > ans { ans = right - left + 1 } }
# 计数 from collections import Counter cnt = Counter(arr)
# 两数之和 seen = {} for i, x inenumerate(arr): if target - x in seen: return [seen[target - x], i] seen[x] = i
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 计数 cnt := map[int]int{} for _, x := range arr { cnt[x]++ }
// 两数之和 seen := map[int]int{} for i, x := range arr { if j, ok := seen[target-x]; ok { return []int{j, i} } seen[x] = i }
7. 栈 (Stack)
适用场景:括号匹配;单调栈处理「下一个更大/更小元素」「柱状图最大矩形」。
1 2 3 4 5 6 7 8
# 单调递减栈(找右边第一个更大的元素) stack = [] res = [-1] * n for i inrange(n): while stack and arr[stack[-1]] < arr[i]: idx = stack.pop() res[idx] = arr[i] stack.append(i)
1 2 3 4 5 6 7 8 9 10 11 12
// 单调递减栈(找右边第一个更大的元素) stack := []int{} res := make([]int, n) for i := range res { res[i] = -1 } for i := 0; i < n; i++ { forlen(stack) > 0 && arr[stack[len(stack)-1]] < arr[i] { idx := stack[len(stack)-1] stack = stack[:len(stack)-1] res[idx] = arr[i] } stack = append(stack, i) }
8. 单调队列 (Monotonic Deque)
适用场景:滑动窗口内的最大/最小值(O(n) 解决)。
1 2 3 4 5 6 7 8 9 10 11 12
from collections import deque dq = deque() # 存下标,保持单调递减(求最大值) for i inrange(n): # 移除超出窗口的元素 if dq and dq[0] < i - k + 1: dq.popleft() # 维护单调性(从尾部移除比当前小的) while dq and arr[dq[-1]] < arr[i]: dq.pop() dq.append(i) if i >= k - 1: ans.append(arr[dq[0]])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 单调队列(滑动窗口最大值) dq := []int{} // 存下标,单调递减 ans := []int{} for i := 0; i < n; i++ { // 移除超出窗口的元素 iflen(dq) > 0 && dq[0] < i-k+1 { dq = dq[1:] } // 维护单调性(从尾部移除比当前小的) forlen(dq) > 0 && arr[dq[len(dq)-1]] < arr[i] { dq = dq[:len(dq)-1] } dq = append(dq, i) if i >= k-1 { ans = append(ans, arr[dq[0]]) } }
visited = set() defdfs(node): visited.add(node) for neighbor in graph[node]: if neighbor notin visited: dfs(neighbor)
1 2 3 4 5 6 7 8 9 10 11
// DFS 连通分量 visited := map[int]bool{} var dfs func(node int) dfs = func(node int) { visited[node] = true for _, neighbor := range graph[node] { if !visited[neighbor] { dfs(neighbor) } } }
拓扑排序(有向无环图 / 依赖关系)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
from collections import deque in_degree = {v: 0for v in graph} for u in graph: for v in graph[u]: in_degree[v] += 1 q = deque([v for v in in_degree if in_degree[v] == 0]) order = [] while q: node = q.popleft() order.append(node) for neighbor in graph[node]: in_degree[neighbor] -= 1 if in_degree[neighbor] == 0: q.append(neighbor)
// 0-1 背包(逆序遍历容量) dp := make([]int, W+1) for _, item := range items { weight, value := item[0], item[1] for w := W; w >= weight; w-- { if dp[w-weight]+value > dp[w] { dp[w] = dp[w-weight] + value } } }
// 完全背包(正序遍历容量) for _, item := range items { weight, value := item[0], item[1] for w := weight; w <= W; w++ { if dp[w-weight]+value > dp[w] { dp[w] = dp[w-weight] + value } } }
区间 DP
1 2 3 4 5 6 7 8
# 戳气球 / 矩阵链乘 dp = [[0] * n for _ inrange(n)] for length inrange(2, n + 1): # 区间长度从小到大 for left inrange(n - length + 1): right = left + length - 1 for k inrange(left, right): # 枚举分割点 dp[left][right] = max(dp[left][right], dp[left][k] + dp[k+1][right] + cost(left, k, right))
1 2 3 4 5 6 7 8 9 10 11 12
// 区间 DP(戳气球 / 矩阵链乘) dp := make([][]int, n) for i := range dp { dp[i] = make([]int, n) } for length := 2; length <= n; length++ { for left := 0; left <= n-length; left++ { right := left + length - 1 for k := left; k < right; k++ { val := dp[left][k] + dp[k+1][right] + cost(left, k, right) if val > dp[left][right] { dp[left][right] = val } } } }
状态压缩 DP
1 2 3 4 5 6 7 8 9 10
# 旅行商问题 (TSP) dp = [[float('inf')] * n for _ inrange(1 << n)] dp[1][0] = 0 for mask inrange(1 << n): for u inrange(n): ifnot (mask >> u & 1): continue for v inrange(n): if mask >> v & 1: continue dp[mask | (1 << v)][v] = min(dp[mask | (1 << v)][v], dp[mask][u] + dist[u][v])
// 快速幂 funcpowMod(base, exp, mod int)int { res := 1 for exp > 0 { if exp&1 == 1 { res = res * base % mod } base = base * base % mod exp >>= 1 } return res }
16. 字符串 (String)
常见手法:
双指针/滑动窗口:最长无重复子串、最小覆盖子串。
哈希:字母异位词分组(以排序字符串为键)。
KMP:O(n) 字符串匹配,构建 next 数组。
1 2 3 4 5 6 7 8 9 10 11
# KMP next 数组 defbuild_next(pattern): next = [0] * len(pattern) j = 0 for i inrange(1, len(pattern)): while j > 0and pattern[i] != pattern[j]: j = next[j-1] if pattern[i] == pattern[j]: j += 1 next[i] = j returnnext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// KMP next 数组 funcbuildNext(pattern string) []int { next := make([]int, len(pattern)) j := 0 for i := 1; i < len(pattern); i++ { for j > 0 && pattern[i] != pattern[j] { j = next[j-1] } if pattern[i] == pattern[j] { j++ } next[i] = j } return next }
17. 位运算 (Bit Manipulation)
技巧
表达式
含义
取最低位 1
x & (-x)
lowbit,树状数组核心
消最低位 1
x & (x-1)
统计 1 的个数
判断奇偶
x & 1
异或消重
a ^ a = 0
找唯一出现奇数次的数
第 k 位
(x >> k) & 1
枚举子集
for sub in range(mask, -1, -1): sub = (sub-1) & mask
状压 DP
1 2 3
# 只出现一次的数(其余出现两次) from functools import reduce return reduce(lambda a, b: a ^ b, nums)
1 2 3 4 5 6 7 8 9 10 11
// 只出现一次的数(其余出现两次) result := 0 for _, x := range nums { result ^= x } return result
// 枚举子集 for sub := mask; sub > 0; sub = (sub - 1) & mask { // 处理子集 sub }