⚠ 当前无法加载公式渲染库(KaTeX 走 CDN),公式可能显示为原始代码——联网后刷新即可。
🏠 学习总导航 · 算法设计与分析 · 在线代码阅读器(手机端免下载直读)

Python 算法核心模板库(practice_algorithms.html)

源文件:practice_algorithms.html(共 323 行) 支持语法高亮 · 一键复制源码 · 护眼暗色模式

1 · 功能说明与手机端直读指南

本文件汇总《算法设计与分析》八大高频核心算法的工业级精简 Python 模板:二分查找左边界、稳定归并排序、最长公共子序列(LCS 滚动数组)、0-1 背包(一维倒序滚动)、区间贪心活动安排、Dijkstra 最短路(最小堆)、n 皇后回溯搜索、Edmonds–Karp 最大流(BFS 增广路)。

为什么提供 HTML 代码阅读器? 在手机或平板浏览器中直接点击 .py 链接通常会触发文件下载而无法直接预览。本页面将 practice_algorithms.html 渲染为带行号、语法高亮与一键复制按钮的标准 V3 网页,方便随时对照课程笔记复习代码细节;同目录下的原始 practice_algorithms.html 仍完整保留供本地 Python 直接运行。

2 · 完整源码:practice_algorithms.html

  1"""算法课 + 求职:可运行 Python 示例,均注明边界与复杂度。"""
  2from bisect import bisect_right
  3from collections import deque
  4from heapq import heappush, heappop
  5
  6
  7def lower_bound(nums, target):
  8    """已排序 nums 的第一个 >= target 的下标。O(log n) 时间。"""
  9    lo, hi = 0, len(nums)
 10    while lo < hi:
 11        mid = (lo + hi) // 2
 12        if nums[mid] < target:
 13            lo = mid + 1
 14        else:
 15            hi = mid
 16    return lo
 17
 18
 19def merge_sort(nums):
 20    """稳定归并排序。O(n log n) 时间、O(n) 额外空间。"""
 21    if len(nums) < 2:
 22        return list(nums)
 23    mid = len(nums) // 2
 24    a, b = merge_sort(nums[:mid]), merge_sort(nums[mid:])
 25    i = j = 0
 26    out = []
 27    while i < len(a) and j < len(b):
 28        if a[i] <= b[j]:
 29            out.append(a[i])
 30            i += 1
 31        else:
 32            out.append(b[j])
 33            j += 1
 34    return out + a[i:] + b[j:]
 35
 36
 37def count_inversions(nums):
 38    """归并分治统计逆序对总数 (i < j 且 nums[i] > nums[j])。O(n log n) 时间。"""
 39    def sort_count(arr):
 40        if len(arr) < 2:
 41            return list(arr), 0
 42        mid = len(arr) // 2
 43        left, c1 = sort_count(arr[:mid])
 44        right, c2 = sort_count(arr[mid:])
 45        merged, cross, i, j = [], 0, 0, 0
 46        while i < len(left) and j < len(right):
 47            if left[i] <= right[j]:
 48                merged.append(left[i])
 49                i += 1
 50            else:
 51                merged.append(right[j])
 52                cross += len(left) - i
 53                j += 1
 54        merged.extend(left[i:])
 55        merged.extend(right[j:])
 56        return merged, c1 + c2 + cross
 57
 58    _, total = sort_count(nums)
 59    return total
 60
 61
 62def quick_select(nums, k):
 63    """三路划分求 0-indexed 第 k 小元素。平均 O(n) 时间。"""
 64    if not 0 <= k < len(nums):
 65        raise ValueError(&#x27;k 越界')
 66    arr = list(nums)
 67    while True:
 68        pivot = arr[len(arr) // 2]
 69        lt = [x for x in arr if x < pivot]
 70        eq = [x for x in arr if x == pivot]
 71        gt = [x for x in arr if x > pivot]
 72        if k < len(lt):
 73            arr = lt
 74        elif k < len(lt) + len(eq):
 75            return pivot
 76        else:
 77            k -= len(lt) + len(eq)
 78            arr = gt
 79
 80
 81def matrix_chain_order(p):
 82    """矩阵连乘最小标量乘法次数,矩阵 A_i 维度为 p[i-1] x p[i]。O(n^3) 时间、O(n^2) 空间。"""
 83    n = len(p) - 1
 84    if n <= 1:
 85        return 0
 86    m = [[0] * (n + 1) for _ in range(n + 1)]
 87    for length in range(2, n + 1):  # 链长 l 从 2 到 n 对角线推进
 88        for i in range(1, n - length + 2):
 89            j = i + length - 1
 90            m[i][j] = min(
 91                m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]
 92                for k in range(i, j)
 93            )
 94    return m[1][n]
 95
 96
 97def lcs_length(a, b):
 98    """LCS 长度。O(len(a)*len(b)) 时间,O(len(b)) 空间。"""
 99    prev = [0] * (len(b) + 1)
100    for ca in a:
101        dp = [0] * (len(b) + 1)
102        for j, cb in enumerate(b, 1):
103            dp[j] = prev[j - 1] + 1 if ca == cb else max(prev[j], dp[j - 1])
104        prev = dp
105    return prev[-1]
106
107
108def max_subarray(nums):
109    """最大非空连续子段和(Kadane 算法)。O(n) 时间、O(1) 空间。"""
110    if not nums:
111        raise ValueError(&#x27;数组不能为空')
112    cur = best = nums[0]
113    for x in nums[1:]:
114        cur = max(x, cur + x)
115        best = max(best, cur)
116    return best
117
118
119def knapsack_01(weights, values, capacity):
120    """正整数重量的 0-1 背包。O(n*capacity) 伪多项式时间。"""
121    if (
122        len(weights) != len(values)
123        or not isinstance(capacity, int)
124        or capacity < 0
125        or any(not isinstance(w, int) or w <= 0 for w in weights)
126    ):
127        raise ValueError(&#x27;输入非法:重量须正整数、容量须非负整数')
128    dp = [0] * (capacity + 1)
129    for w, v in zip(weights, values):
130        for c in range(capacity, w - 1, -1):  # 倒序防止一个物品被重复选
131            dp[c] = max(dp[c], dp[c - w] + v)
132    return dp[capacity]
133
134
135def optimal_bst(p, q):
136    """最优二叉查找树期望代价,p[0..n-1] 为实结点概率,q[0..n] 为伪结点概率。O(n^3) 时间。"""
137    n = len(p)
138    e = [[0.0] * (n + 2) for _ in range(n + 2)]
139    w = [[0.0] * (n + 2) for _ in range(n + 2)]
140    for i in range(1, n + 2):
141        e[i][i - 1] = w[i][i - 1] = q[i - 1]
142    for length in range(1, n + 1):
143        for i in range(1, n - length + 2):
144            j = i + length - 1
145            w[i][j] = w[i][j - 1] + p[j - 1] + q[j]
146            e[i][j] = min(
147                e[i][r - 1] + e[r + 1][j] + w[i][j]
148                for r in range(i, j + 1)
149            )
150    return e[1][n]
151
152
153def activity_selection(intervals):
154    """最多不重叠区间 [start,end)。O(n log n) 时间。"""
155    if any(t < s for s, t in intervals):
156        raise ValueError(&#x27;结束早于开始')
157    ans, last = [], float(&#x27;-inf')
158    for s, t in sorted(intervals, key=lambda x: x[1]):
159        if s >= last:
160            ans.append((s, t))
161            last = t
162    return ans
163
164
165def weighted_interval_scheduling(intervals):
166    """带权活动安排 (start, end, weight) 最大总权重。O(n log n) 时间。"""
167    if not intervals:
168        return 0
169    jobs = sorted(intervals, key=lambda x: x[1])
170    ends = [j[1] for j in jobs]
171    dp = [0] * (len(jobs) + 1)
172    for i, (s, _, w) in enumerate(jobs, 1):
173        p = bisect_right(ends, s, 0, i - 1)
174        dp[i] = max(dp[i - 1], dp[p] + w)
175    return dp[-1]
176
177
178def dijkstra(graph, source):
179    """邻接表 (v,weight) 的非负最短路。堆 O((V+E)log V)。"""
180    n = len(graph)
181    if not 0 <= source < n:
182        raise ValueError(&#x27;源点越界')
183    if any(v < 0 or v >= n or w < 0 for edges in graph for v, w in edges):
184        raise ValueError(&#x27;端点越界或存在负权')
185    dist = [float(&#x27;inf')] * n
186    dist[source] = 0
187    pq = [(0, source)]
188    while pq:
189        d, u = heappop(pq)
190        if d > dist[u]:
191            continue
192        for v, w in graph[u]:
193            new = d + w
194            if new < dist[v]:
195                dist[v] = new
196                heappush(pq, (new, v))
197    return dist
198
199
200def n_queens(n):
201    """每种方案为逐行皇后列号,最坏指数搜索。"""
202    if n < 0:
203        raise ValueError(&#x27;n 不得为负')
204    ans, path = [], []
205    cols, d1, d2 = set(), set(), set()
206
207    def search(row):
208        if row == n:
209            ans.append(path[:])
210            return
211        for col in range(n):
212            a, b = row - col, row + col
213            if col in cols or a in d1 or b in d2:
214                continue
215            cols.add(col)
216            d1.add(a)
217            d2.add(b)
218            path.append(col)
219            search(row + 1)
220            path.pop()
221            d2.remove(b)
222            d1.remove(a)
223            cols.remove(col)
224
225    search(0)
226    return ans
227
228
229def knapsack_branch_and_bound(weights, values, capacity):
230    """优先队列式分支限界求解 0-1 背包最优值(按单位价值线性松弛上界剪枝)。"""
231    items = sorted(zip(weights, values), key=lambda x: x[1] / x[0], reverse=True)
232    n = len(items)
233
234    def bound(idx, cur_w, cur_v):
235        rem = capacity - cur_w
236        b = float(cur_v)
237        while idx < n and items[idx][0] <= rem:
238            rem -= items[idx][0]
239            b += items[idx][1]
240            idx += 1
241        if idx < n and rem > 0:
242            b += rem * (items[idx][1] / items[idx][0])
243        return b
244
245    best = 0
246    pq = [(-bound(0, 0, 0), 0, 0, 0)]  # (-ub, idx, cur_w, cur_v)
247    while pq:
248        neg_ub, idx, cur_w, cur_v = heappop(pq)
249        if -neg_ub <= best or idx == n:
250            continue
251        w, v = items[idx]
252        if cur_w + w <= capacity:
253            nv = cur_v + v
254            if nv > best:
255                best = nv
256            ub_left = bound(idx + 1, cur_w + w, nv)
257            if ub_left > best:
258                heappush(pq, (-ub_left, idx + 1, cur_w + w, nv))
259        ub_right = bound(idx + 1, cur_w, cur_v)
260        if ub_right > best:
261            heappush(pq, (-ub_right, idx + 1, cur_w, cur_v))
262    return best
263
264
265def edmonds_karp(capacities, source, sink):
266    """有向图容量矩阵最大流,允许整数容量。Edmonds–Karp O(VE²)。"""
267    n = len(capacities)
268    if not n or source == sink or not 0 <= source < n or not 0 <= sink < n:
269        raise ValueError(&#x27;源汇不合法')
270    if any(len(row) != n or any(c < 0 or not isinstance(c, int) for c in row) for row in capacities):
271        raise ValueError(&#x27;容量须非负整数方阵')
272    res = [list(row) for row in capacities]
273    total = 0
274    while True:
275        parent = [-1] * n
276        parent[source] = source
277        todo = deque([source])
278        while todo and parent[sink] == -1:
279            u = todo.popleft()
280            for v, c in enumerate(res[u]):
281                if c > 0 and parent[v] == -1:
282                    parent[v] = u
283                    todo.append(v)
284                    if v == sink:
285                        break
286        if parent[sink] == -1:
287            break
288        v = sink
289        delta = float(&#x27;inf')
290        while v != source:
291            u = parent[v]
292            delta = min(delta, res[u][v])
293            v = u
294        v = sink
295        while v != source:
296            u = parent[v]
297            res[u][v] -= delta
298            res[v][u] += delta
299            v = u
300        total += delta
301    return total
302
303
304def vertex_cover_approx(edges):
305    """极大匹配 2-近似顶点覆盖算法。O(V + E) 时间,近似比 ρ = 2。"""
306    covered = set()
307    cover = []
308    for u, v in edges:
309        if u not in covered and v not in covered:
310            covered.add(u)
311            covered.add(v)
312            cover.extend([u, v])
313    return cover
314
315
316if __name__ == &#x27;__main__':
317    print(&#x27;二分:', lower_bound([1, 2, 2, 4], 2))
318    print(&#x27;逆序对:', count_inversions([2, 3, 8, 6, 1]))
319    print(&#x27;矩阵连乘:', matrix_chain_order([30, 35, 15, 5, 10, 20, 25]))
320    print(&#x27;LCS:', lcs_length('ABCBDAB', 'BDCABA'))
321    print(&#x27;背包(DP/BB):', knapsack_01([2, 2, 6, 5, 4], [6, 3, 5, 4, 6], 10),
322          knapsack_branch_and_bound([2, 2, 6, 5, 4], [6, 3, 5, 4, 6], 10))
323    print(&#x27;4 皇后:', len(n_queens(4)))

3 · 核心实现要点与复杂度对照

函数名对应课程模块时间 / 空间复杂度笔试与机试关键防坑点
lower_bound(nums, target)模块 1 · 二分搜索\(O(\log n)\) / \(O(1)\)半开区间 [lo, hi) 循环不变式,返回首个 \(\ge ext{target}\) 的下标
merge_sort(nums)模块 1 · 分治排序\(O(n\log n)\) / \(O(n)\)合并时 a[i] <= b[j] 取等号保证排序稳定性
lcs_length(a, b)模块 2 · 动态规划 LCS\(O(mn)\) / \(O(n)\)仅用两行一维数组滚动更新,将空间从 \(O(mn)\) 降至 \(O(n)\)
knapsack_01(weights, values, capacity)模块 2 · 0-1 背包 DP\(O(nC)\) 伪多项式 / \(O(C)\)内层容量循环必须倒序 range(capacity, w-1, -1),防止同件物品重复装入
activity_selection(intervals)模块 2 · 贪心算法\(O(n\log n)\) / \(O(n)\)(排序副本及返回结果)严格按结束时间 x[1] 升序排序,兼容条件为 s >= last
dijkstra(graph, source)模块 2 · 单源最短路\(O((V+E)\log(V+E))\) / \(O(V+E)\)(懒删除堆,输入图不计)要求所有边权非负;出堆时检查 if d > dist[u]: continue 跳过过期懒删除结点
n_queens(n)模块 3 · 回溯法排列/子集树指数级 / 栈深 \(O(n)\)用三个集合 cols, d1(r-c), d2(r+c) \(O(1)\) 判冲突,递归返回后务必恢复现场
edmonds_karp(capacities, source, sink)模块 4 · 网络流最大流矩阵扫描版上界 \(O(V^3E)\) / \(O(V^2)\);经典邻接表实现为 \(O(VE^2)\)BFS 寻找最短增广路径;沿增广路同时更新正向残量 -= delta 与反向退流边 += delta

实现口径:上表空间包含排序副本、队列及结果容器(不计输入本身);本页的 Edmonds–Karp Python 示例采用容量矩阵,BFS 每轮扫描 \(V^2\) 个矩阵单元,不能把邻接表教材复杂度直接当成这份实现的紧致界。quick_select 采用固定中间位置元素作轴值,未随机化,存在最坏 \(O(n^2)\) 的输入;分支限界背包示例假定正重量、非负价值,且两数组长度一致。

配套单元测试请查看:✅ 算法单元测试与暴力对拍器(test_algorithms.html)。