图论算法核心:存储、遍历、最短路径与最小生成树实战解析 1. 从迷宫到网络图论算法为何是程序员的必修课如果你玩过《塞尔达传说》或者任何一款迷宫游戏你肯定有过这样的经历站在一个岔路口面前有三条路你需要决定走哪条才能最快找到宝箱或者出口。这个看似简单的“选择”背后其实就隐藏着图论算法的核心思想。在程序的世界里我们每天都在处理类似的“迷宫”社交网络里谁是谁的朋友社交图谱、地图软件里如何规划最短路径导航算法、电商平台如何给你推荐商品协同过滤、甚至编译器如何优化代码的执行顺序控制流图。这些看似风马牛不相及的问题都可以抽象成“图”这个数据结构并用一套通用的算法工具来解决。今天我们不谈枯燥的数学定义就从几个你肯定遇到过或即将遇到的真实场景出发掰开揉碎地讲讲那些支撑起现代数字世界的图论相关算法。无论你是正在刷题准备面试的新手还是需要解决实际工程问题的老手掌握这些算法就相当于获得了一张解开复杂系统关联性的万能地图。2. 图的“灵魂”两种存储方式与你的选型困境在动手写任何图算法之前第一个拦路虎往往是如何把图“装”进计算机里。这直接决定了后续所有操作的效率上限。主流有两种方式邻接矩阵和邻接表。很多教程只告诉你“稀疏图用邻接表稠密图用邻接矩阵”但为什么以及在实际项目中到底怎么选这里面的门道可不少。2.1 邻接矩阵直观的“城市公交总图”想象一个城市有N个公交站点邻接矩阵就像一个巨大的N×N表格。表格的第i行第j列的值就表示从站点i到站点j有没有直达公交车有权图则是车费或时间。用代码表示就是一个二维数组matrix[i][j]。# 假设有5个顶点0-4构建一个无向图的邻接矩阵 V 5 graph_matrix [[0] * V for _ in range(V)] # 添加边0-1, 0-4, 1-2, 1-3, 1-4, 2-3, 3-4 edges [(0,1), (0,4), (1,2), (1,3), (1,4), (2,3), (3,4)] for u, v in edges: graph_matrix[u][v] 1 graph_matrix[v][u] 1 # 无向图需要对称设置 print(graph_matrix[0]) # 输出顶点0的邻居情况[0, 1, 0, 0, 1]它的优势极其明显查询速度极快判断任意两个顶点u和v是否直接相连即是否有边只需要O(1)的时间访问matrix[u][v]。这在某些需要频繁进行“存在性检查”的场景下是无可替代的。适合稠密图当图的边数量接近顶点数量的平方时即几乎每个点都和其他点相连邻接矩阵的空间利用率很高因为几乎每个格子都被用上了。易于理解和实现结构非常规整对于某些基于矩阵运算的图算法如通过矩阵乘法计算路径有天然优势。但它的代价也同样沉重空间消耗巨大空间复杂度是O(V^2)。对于一个有10000个顶点的社交网络哪怕只有几万个好友关系稀疏你也需要维护一个1亿10000*10000大小的二维数组其中绝大部分都是0这是巨大的浪费。添加/删除顶点成本高动态增加一个顶点需要重新分配并复制整个矩阵成本是O(V^2)。注意在面试或算法竞赛中如果题目明确顶点数V 500或1000邻接矩阵通常是安全且编码简单的选择。但一旦V上万就要立刻警惕。2.2 邻接表高效的“个人通讯录”邻接表则采用了完全不同的思路。它为每个顶点维护一个列表链表、动态数组等这个列表里只存储该顶点的直接邻居。还是那个公交城市的例子现在你只拥有一本“个人通讯录”记录从你家某个顶点出发能坐哪几路车分别到哪些邻居家。from collections import defaultdict V 5 graph_adj_list defaultdict(list) # 使用字典存储键为顶点值为邻居列表 edges [(0,1), (0,4), (1,2), (1,3), (1,4), (2,3), (3,4)] for u, v in edges: graph_adj_list[u].append(v) graph_adj_list[v].append(u) # 无向图 print(graph_adj_list[0]) # 输出顶点0的邻居列表[1, 4] print(graph_adj_list[1]) # 输出顶点1的邻居列表[0, 2, 3, 4]邻接表的优势在于空间效率高存储空间为O(V E)其中E是边数。对于稀疏图E远小于V^2这比邻接矩阵节省了海量内存。现代互联网上的图99%都是稀疏图。遍历邻居高效要遍历某个顶点的所有邻居直接遍历其列表即可时间复杂度是O(degree(v))其中degree(v)是该顶点的邻居数。这对于BFS/DFS等需要遍历边的算法是最高效的。动态增删灵活添加边和顶点相对容易。它的缺点则是查询边存在性慢判断边(u, v)是否存在需要遍历u的邻居列表最坏情况O(degree(u))。如果必须频繁进行此操作可能需要结合哈希集合来优化。实现稍复杂相比矩阵的规整邻接表的结构更松散调试时直观性稍差。2.3 实战选型一个真实的踩坑案例我曾经参与一个社交网络“共同好友”功能的初期开发。最初为了图省事我用了邻接矩阵因为判断“A和B是否是好友”这个操作太方便了。当用户量突破10万时服务内存直接爆了。那个100000 x 100000的矩阵即使用boolean类型1字节也轻松吃掉近100GB内存而实际好友关系边只有几百万条。重构方案我们换成了邻接表每个用户的ID作为键其好友ID列表作为值存储在Redis的Hash结构中。内存骤降到几百MB。对于“判断是否为好友”这个高频操作我们在每个用户的好友列表外额外维护了一个Redis Set作为快速查询的索引。虽然增加了一点写操作的成本需要同时更新列表和集合但换来了O(1)的查询和O(VE)的内存这是典型的“以空间换时间”策略在工程上的灵活变通。给你的建议在绝大多数应用开发中邻接表是默认且安全的选择。除非你非常确定图是稠密的或者顶点数极少且需要极快的随机边查询。在算法题中根据顶点规模灵活选择通常V 5000就该优先考虑邻接表。3. 图的“探索”深度与广度优先搜索远不止遍历那么简单DFS深度优先搜索和BFS广度优先搜索是图论算法世界的“原子操作”是几乎所有高级算法的基础。但很多人学了之后只记得“用栈”、“用队列”却不知道在什么场景下该用谁以及如何利用它们解决实际问题。3.1 DFS深入虎穴的探险家与回溯算法DFS的策略是“一条路走到黑”就像走迷宫时遇到岔路口就随便选一条路走下去直到死胡同再退回上一个岔路口选另一条路。它的递归结构天然适合处理“探索所有可能路径”的问题。核心应用场景连通分量计数判断一个无向图中有几个互相不连通的“子图”。这是很多社交网络分析、图像分割的底层原理。拓扑排序用于有向无环图DAG解决任务调度、编译顺序等依赖问题。DFS可以实现一个非常优雅的拓扑排序在递归返回时将顶点入栈最后栈中序列就是逆拓扑序。检测环尤其是在有向图中通过DFS过程中标记节点的状态未访问、访问中、已访问可以高效检测图中是否存在环这是任务调度系统避免死锁的关键。回溯算法基础诸如八皇后、数独、全排列等问题本质上是在一个隐式的“状态空间图”上进行DFS寻找满足条件的路径。DFS递归模板务必掌握visited set() # 记录已访问节点避免重复访问和死循环 def dfs(node): if node in visited: return # 处理当前节点 print(fVisiting {node}) visited.add(node) # 遍历所有邻居 for neighbor in graph_adj_list[node]: dfs(neighbor) # 对于非连通图需要遍历所有节点作为起点 for node in range(V): if node not in visited: dfs(node)一个DFS的典型问题寻找所有路径。假设你要从一个城市到另一个城市想找出所有不重复城市的旅行方案。DFS非常适合因为它会系统地探索每一条分支。def find_all_paths(graph, start, end, path[]): path path [start] # 创建当前路径的副本 if start end: return [path] # 找到一条完整路径 if start not in graph: return [] paths [] for neighbor in graph[start]: if neighbor not in path: # 避免回路 new_paths find_all_paths(graph, neighbor, end, path) for p in new_paths: paths.append(p) return paths3.2 BFS层层推进的雷达与最短路径基石BFS的策略是“地毯式搜索”从起点开始先访问所有直接邻居再访问邻居的邻居以此类推。它保证在无权图中第一次访问到某个节点时走过的路径就是最短路径。核心应用场景无权图最短路径这是BFS的招牌应用。比如在社交网络中计算“六度空间”两个人之间最少通过多少人认识或者在迷宫游戏中找最短出口路径。层级遍历或扩散网络爬虫按距离种子网址的“跳数”一层层抓取传染病传播模型模拟图像填充算法。检测二分图通过BFS或DFS对节点进行“染色”如果相邻节点颜色冲突则不是二分图。这在分配问题、广告投放匹配中有应用。BFS队列模板务必掌握from collections import deque def bfs(start): visited set([start]) queue deque([start]) while queue: node queue.popleft() print(fProcessing {node}) # 处理当前节点 # 注意在这里node的层级就是它距离起点的最短距离无权图 for neighbor in graph_adj_list[node]: if neighbor not in visited: visited.add(neighbor) queue.append(neighbor)BFS找最短路径长度示例def shortest_path_length(graph, start, end): if start end: return 0 visited set([start]) queue deque([(start, 0)]) # (节点, 距离) while queue: node, dist queue.popleft() for neighbor in graph[node]: if neighbor end: return dist 1 if neighbor not in visited: visited.add(neighbor) queue.append((neighbor, dist 1)) return -1 # 不可达3.3 DFS vs BFS如何选择一个决策框架很多新手会混淆。记住这个简单的决策链问题是否要求“最短”或“最少步数”是- 优先考虑BFS无权图或Dijkstra有权图。否- 进入下一步。问题是否需要遍历或检测图中的“连通性”、“环”、“拓扑序”是-DFS通常编码更简洁。问题是否需要“回溯”或“探索所有可能组合/排列”是- 这是DFS回溯的绝对领域。图的结构是否非常深分支少但路径长且可能答案在较浅层是- 使用BFS避免DFS陷入过深分支。反之如果图很宽BFS队列可能消耗大量内存DFS可能更合适。实操心得在解决具体问题时我经常先问自己“我要找的是什么是一条可行解DFS常用于找解还是最优解BFS常用于无权图最优” 同时考虑图的规模。如果图深度可能极大比如1万层递归DFS可能导致栈溢出需要显式使用栈来实现迭代DFS。而BFS的空间复杂度在最坏情况下是O(V)在图很宽时需要注意。4. 加权图的“最优解”Dijkstra与它的朋友们当图中的边有了权重比如距离、时间、成本BFS就失效了因为它默认每走一步代价相同。这时我们需要更强大的算法。Dijkstra算法是解决单源最短路径问题从一个点到图中所有其他点的最短路径最著名、最实用的算法。它的核心思想是“贪心”每次从未确定的节点中选择一个距离起点最近的节点确认它的最短距离并用它来更新其邻居的距离。4.1 Dijkstra算法核心流程与手动模拟我们用一个经典例子来看求从顶点A到其他各点的最短距离。 假设图如下邻接表表示A - [(B, 1), (C, 4)] B - [(C, 2), (D, 6)] C - [(D, 3)] D - []步骤初始化起点A距离为0其他点距离为无穷大(∞)。所有点标记为“未确定”。dist {A:0, B:∞, C:∞, D:∞}第一轮从未确定节点{A(0), B(∞), C(∞), D(∞)}中选出距离最小的A(0)。确认A的最短距离就是0。用A更新其邻居B:min(∞, 01) 1C:min(∞, 04) 4dist {A:0, B:1, C:4, D:∞}第二轮未确定节点{B(1), C(4), D(∞)}中最小是B(1)。确认B的最短距离为1。用B更新邻居C:min(4, 12) 3(发现经过B到C更短)D:min(∞, 16) 7dist {A:0, B:1, C:3, D:7}第三轮未确定节点{C(3), D(7)}中最小是C(3)。确认C的最短距离为3。用C更新邻居D:min(7, 33) 6dist {A:0, B:1, C:3, D:6}第四轮确认最后一个未确定节点D(6)。算法结束。最终从A到各点的最短距离为A:0, B:1, C:3, D:6。4.2 优先级队列实现效率的关键上述手动过程需要反复从集合中找最小值朴素实现是O(V^2)。工程上我们使用最小堆优先级队列来优化这个“找最小”的过程可以将复杂度降至O((VE) log V)对于稀疏图效率提升巨大。import heapq def dijkstra(graph, start): # 初始化距离字典所有点距离为无穷大 dist {node: float(inf) for node in graph} dist[start] 0 # 使用最小堆存储 (距离, 节点) pq [(0, start)] while pq: current_dist, current_node heapq.heappop(pq) # 如果当前取出的距离大于已知最短距离说明是旧数据跳过 if current_dist dist[current_node]: continue # 遍历邻居 for neighbor, weight in graph[current_node]: distance current_dist weight # 如果找到更短的路径 if distance dist[neighbor]: dist[neighbor] distance heapq.heappush(pq, (distance, neighbor)) return dist这段代码有几个关键点if current_dist dist[current_node]: continue这行是性能优化的精髓。因为同一个节点可能被多次加入堆每次找到更短距离时但只有最早弹出即距离最小的那次是有效的后续弹出的都是“过时”的、更长的距离直接跳过。使用(距离, 节点)作为堆元素Python的heapq默认按元组第一个元素排序正好符合需求。算法结束后dist字典就包含了从起点到所有可达节点的最短距离。4.3 Dijkstra的局限性负权边与A*启发式搜索Dijkstra算法有一个致命弱点无法处理含有负权边的图。为什么因为它的贪心策略基于一个假设“当前距离最短的节点其最短距离已经确定”。一旦存在负权边这个假设就不成立了因为未来可能通过一条负权边让这个“已确定”节点的距离变得更短。对于带负权边的图需要使用Bellman-Ford或SPFA算法。另一个常见变种是A*搜索算法。你可以把A理解为“带导航的Dijkstra”。Dijkstra是盲目地向所有方向均匀探索而A则引入了一个启发式函数h(n)用来估计从当前节点n到目标节点的代价。优先级队列的排序依据从f(n) g(n)实际代价变成了f(n) g(n) h(n)实际估计。只要启发函数h(n)是可采纳的即永远不会高估实际代价A就能保证找到最短路径并且通常比Dijkstra探索的节点少得多效率更高。地图导航软件就是A的典型应用h(n)常选用两点间的直线距离欧几里得距离或曼哈顿距离。踩坑提醒实现Dijkstra时务必确保你的图没有负权边。在业务中如果是计算物理距离、时间成本通常不会出现负数。但如果是计算利润、得分有正有负就需要换用其他算法。另外使用优先级队列时别忘了上面提到的“跳过旧数据”的判断这是保证正确性和效率的关键。5. 最小生成树用最少的线连接所有的点想象你要为一个新建小区的所有房屋铺设光纤网络要求所有房屋都能联网连通并且使用的光纤总长度最短。这就是最小生成树Minimum Spanning Tree, MST的经典问题。它要在无向连通图中找出一棵包含所有顶点的树使得树上所有边的权重之和最小。5.1 Kruskal算法并查集的绝佳舞台Kruskal算法的思想非常直观从小到大考虑所有边如果这条边连接了两个尚未连通的部件就选中它否则就跳过。这需要一种高效的数据结构来判断两个顶点是否已经连通——这就是并查集Union-Find。算法步骤将图中所有边按权重从小到大排序。初始化一个并查集每个顶点自成一个集合。按顺序遍历排序后的边。对于每条边(u, v, w)用并查集检查u和v是否已经在同一个集合中即已连通。如果不在则选中这条边并将u和v所在的集合合并。如果已经在则跳过避免形成环。当选中边的数量达到V-1条时一棵树的边数算法结束。class UnionFind: def __init__(self, n): self.parent list(range(n)) self.rank [0] * n def find(self, x): if self.parent[x] ! x: self.parent[x] self.find(self.parent[x]) # 路径压缩 return self.parent[x] def union(self, x, y): rootX, rootY self.find(x), self.find(y) if rootX rootY: return False # 按秩合并 if self.rank[rootX] self.rank[rootY]: self.parent[rootX] rootY elif self.rank[rootX] self.rank[rootY]: self.parent[rootY] rootX else: self.parent[rootY] rootX self.rank[rootX] 1 return True def kruskal(n, edges): # edges: list of (weight, u, v) uf UnionFind(n) edges.sort() # 按权重排序 mst_weight 0 mst_edges [] for weight, u, v in edges: if uf.union(u, v): # 如果成功合并说明边被选中 mst_weight weight mst_edges.append((u, v, weight)) if len(mst_edges) n - 1: break return mst_weight, mst_edgesKruskal的适用场景非常适合边比较稀疏的图。因为它的时间复杂度主要取决于边的排序O(E log E)后续的并查集操作接近常数时间。5.2 Prim算法从一点开始生长的贪心Prim算法的思路和Dijkstra很像但它生长的是“一棵树”而不是“最短路径”。它从一个任意顶点开始每次将连接当前树与树外顶点的权重最小的边以及该边对应的新顶点加入到树中。算法步骤使用优先级队列优化任选一个起始顶点将其加入最小生成树集合MST_Set。将这个顶点的所有邻接边终点不在MST_Set中加入一个最小堆。循环直到MST_Set包含所有顶点从堆中弹出权重最小的边(weight, u, v)其中u在MST_Set中v不在。将v加入MST_Set这条边加入MST。将v的所有邻接边终点不在MST_Set中加入堆。注意和Dijkstra一样同一条边可能被多次加入堆需要判断终点是否已在集合内。import heapq def prim(n, graph): # graph: adjacency list, graph[u] [(v, weight), ...] visited [False] * n mst_weight 0 mst_edges [] # 从顶点0开始 pq [] # (weight, u, v) visited[0] True for v, w in graph[0]: heapq.heappush(pq, (w, 0, v)) while pq and len(mst_edges) n - 1: weight, u, v heapq.heappop(pq) if visited[v]: continue # 跳过已访问的顶点 visited[v] True mst_weight weight mst_edges.append((u, v, weight)) # 将新顶点v的边加入堆 for next_v, next_w in graph[v]: if not visited[next_v]: heapq.heappush(pq, (next_w, v, next_v)) if len(mst_edges) ! n - 1: return None, None # 图不连通无法生成MST return mst_weight, mst_edgesPrim的适用场景非常适合边比较稠密的图。它的时间复杂度为O(E log V)使用斐波那契堆可以优化到O(E V log V)但在竞赛和一般工程中优先级队列的实现已经足够好。5.3 Kruskal vs Prim如何选择这又是一个常见的选型问题。我的经验法则是看图的稠密程度如果图近乎完全图边数E ≈ V^2Prim算法尤其是邻接矩阵实现更有优势。如果图很稀疏E ≈ V或V log VKruskal算法更简洁高效。看实现复杂度Kruskal需要写好并查集但一旦写好算法主体非常清晰。Prim需要维护一个不断增长的树和堆逻辑稍复杂一点。看输入格式如果给你的就是边的列表用Kruskal省去了建图的步骤。如果给的是邻接表或邻接矩阵Prim可能更方便。实操心得在大多数编程竞赛中因为图通常以边列表形式给出且不特别稠密所以Kruskal是更通用的选择。但在实际工程项目中比如网络布线、芯片设计图的结构可能更复杂需要根据具体情况分析。一个简单的记忆方法是“边少用Kruskal边多用Prim”。另外务必注意算法前提图必须是无向连通图。如果图不连通得到的是“最小生成森林”。6. 拓扑排序解开任务依赖的死结当你有一系列任务某些任务必须在另一些任务完成之后才能开始比如编译代码时模块A依赖模块B就必须先编译B你如何找到一个合理的执行顺序保证所有依赖都被满足这就是拓扑排序要解决的问题。它只适用于有向无环图DAG。6.1 Kahn算法基于入度的广度优先策略Kahn算法非常直观模拟了一个“不断移除没有前置依赖的任务”的过程。计算每个顶点的入度有多少条边指向它。将所有入度为0的顶点加入一个队列。当队列不为空时弹出队首顶点u将其加入拓扑序。遍历u的所有出边(u - v)将v的入度减1。如果v的入度减为0则将v入队。如果最终拓扑序中的顶点数等于图中总顶点数则排序成功否则说明图中存在环无法进行拓扑排序。from collections import deque def topological_sort_kahn(graph, n): # graph: adjacency list, graph[u] [v, ...] 代表 u - v 的边 in_degree [0] * n # 计算入度 for u in range(n): for v in graph[u]: in_degree[v] 1 queue deque([i for i in range(n) if in_degree[i] 0]) topo_order [] while queue: u queue.popleft() topo_order.append(u) for v in graph[u]: in_degree[v] - 1 if in_degree[v] 0: queue.append(v) if len(topo_order) n: return topo_order # 有效拓扑序 else: return [] # 图中有环Kahn算法的优点容易理解便于检测环。如果最后还有顶点入度不为0说明这些顶点构成了环的一部分。6.2 基于DFS的算法递归与后序的巧妙结合另一种方法利用DFS的递归特性。对一个顶点进行DFS只有当它的所有后继节点都访问完成后才将其加入结果列表。最后将结果列表反转即得到拓扑序。def topological_sort_dfs(graph, n): visited [0] * n # 0未访问, 1访问中, 2已访问 topo_order [] def dfs(u): if visited[u] 1: # 遇到访问中的节点说明有环 return False if visited[u] 2: return True visited[u] 1 # 标记为访问中 for v in graph[u]: if not dfs(v): return False visited[u] 2 # 标记为已访问 topo_order.append(u) # 在递归返回时加入顺序是逆序的 return True for i in range(n): if visited[i] 0: if not dfs(i): return [] # 有环 return topo_order[::-1] # 反转得到拓扑序DFS方法的优点代码紧凑利用递归栈天然实现了“后序”处理。状态数组visited用三种状态巧妙地实现了环的检测。6.3 拓扑排序的应用远不止任务调度课程安排LeetCode经典题目“课程表”就是拓扑排序的直接应用。构建工具如Make, Maven, Gradle等确定源码编译顺序。事件序列化在数据库或分布式系统中确定具有依赖关系的事务的执行顺序。公式计算在电子表格中计算单元格公式时需要先计算被引用的单元格。依赖解析软件包管理器如apt, yum, npm解决库依赖关系。注意事项拓扑排序的结果不唯一。一个DAG可能有多个合法的拓扑序。Kahn算法和DFS算法产生的顺序可能不同这取决于顶点处理的顺序如队列的初始顺序、图的存储顺序等。这在某些场景下很重要比如你希望任务尽可能并行执行可能需要寻找一种特定的拓扑序。另外务必在算法中加入环检测因为现实中的数据可能包含循环依赖你的程序需要能优雅地报告错误而不是死循环或输出错误结果。

相关新闻

最新新闻

C++ CRTP模式:从静态多态到表达式模板的编译期优化实践

C++ CRTP模式:从静态多态到表达式模板的编译期优化实践

1. 项目概述:从“奇技淫巧”到现代C基石第一次听说CRTP(Curiously Recurring Template Pattern,奇异递归模板模式)这个名字,是在一个关于静态多态的性能优化讨论里。当时的感觉是,这名字起得真够“奇异”的…

2026/8/23 7:41:07
经典系统+AI算法:老题目也能选出创新点

经典系统+AI算法:老题目也能选出创新点

经典系统AI算法:老题目也能选出创新点 聊完选题趋势之后,很多同学会有一个顾虑:AI方向听起来好,但从零想一个"纯AI场景"的选题,风险其实不低——业务场景不成熟、需求边界模糊,反而容易踩进上一…

2026/8/23 7:41:07
开源跨平台SSH工具全解析:集成数据库管理、云端同步的远程工作台

开源跨平台SSH工具全解析:集成数据库管理、云端同步的远程工作台

最近在折腾服务器运维和远程开发时,发现手头的 SSH 客户端工具要么功能单一,要么界面老旧,要么就是商业软件价格不菲。尤其是在需要同时管理多台服务器、查看数据库、同步配置时,不得不在多个工具间来回切换,效率低下。…

2026/8/23 7:41:07
虎鲸 AICheck 语义断言实战:杜绝“假绿“回归

虎鲸 AICheck 语义断言实战:杜绝“假绿“回归

回归测试最危险的不是"用例挂了",是"用例假绿了"——报告一片绿,线上却出了事故。 "假绿"的根因,往往出在断言太弱:只要元素在页面上、只要接口返回 200,就算过。至于业务对不对、数据有…

2026/8/23 7:41:07
程序员招聘市场现状与应对策略分析

程序员招聘市场现状与应对策略分析

1. 程序员招聘市场的现状观察最近半年,身边不少同行都在讨论同一个现象:技术岗位的招聘要求越来越离谱。上周看到某大厂放出的中级Java开发岗,要求候选人"精通分布式系统设计,有高并发项目经验,同时掌握Python和G…

2026/8/23 7:41:07
Kimi    LeetCode LCP 15. 游乐园的迷宫 Rust实现

Kimi LeetCode LCP 15. 游乐园的迷宫 Rust实现

根据已收集的信息,我来为你提供 LCP 15. 游乐园的迷宫 的 Rust 实现。题目分析这道题是贪心 计算几何问题。核心思想是:> 每次选择一个"极端"的点,使得剩余未访问的点全部位于当前转向方向要求的一侧,从而保证后续每…

2026/8/23 7:36:07