BFS (1)算法模板 看是否需要分层 (2)拓扑排序——检测编译时的循环依赖 制定有依赖关系的任务的执行顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | BFS模板,记住这 5 个: ( 1 )针对树的BFS 1.1 无需分层遍历 from collections import deque def levelOrderTree(root): if not root: return q = deque([root]) while q: head = q.popleft() do something with this head node... if head.left: q.append(head.left) if head.right: q.append(head.right) return xxx 1.2 需要分层遍历 def levelOrderTree(root): if not root: return q = [root] while q: new_q = [] for node in q: # 和上面代码相比 差异就在这里 和 deque do something with this layer nodes... if node.left: new_q.append(node.left) if node.right: new_q.append(node.right) q = new_q return xxx ( 2 )针对图的BFS 2.1 无需分层遍历 from collections import deque def bfs_graph(root): if not root: return queue = deque([root]) seen = set ([root]) while queue: head = queue.popleft() do something with this head... for neighbor in head.neighbors: if neighbor not in seen: # 和tree的区别无非就是多了一个是否访问过的判断 seen.add(neighbor) queue.append(neighbor) return xxx 上述代码中: neighbor 表示从某个点 head 出发,可以走到的下一层的节点。 set / seen 存储已经访问过的节点(已经丢到 queue 里去过的节点) queue 存储等待被拓展到下一层的节点 set / seen 与 queue 是一对好基友,无时无刻都一起出现,往 queue 里新增一个节点,就要同时丢到 set 里。 需要分层遍历的宽度搜先搜索 2.2 需分层遍历【较为少见,可以先不看】 def bfs_graph(root): if not root: return [] q = [root] seen = set ([root]) while q: new_q = [] for node in q: do something with this layer nodes... for neighbor in node.neighbors: if neighbor not in seen: # 和tree的区别无非就是多了一个是否访问过的判断 seen.add(neighbor) new_q.append(neighbor) q = new_q return xxx ( 3 )拓扑排序 = = 》仅仅针对有向无环图,!!!如果是有环的图,则生成的排序仅仅能够完成无环的点,有环路的点因为入度始终无法为 0 ,所以不会加入到排序结果! = = 》体会精髓!!! <br>更新代码模板: 20230208 果然还是比我的简洁 q = [u for u, d in inDeg.items() if d = = 0 ] for u in q: for v in g[u]: inDeg[v] - = 1 if inDeg[v] = = 0 : q.append(v) return q 之前的模板<br> 记住下面的代码 class Solution: """ @param graph: A list of Directed graph node @return: Any topological order for the given graph. """ def topSort( self , graph): node_to_indegree = self .get_indegree(graph) # bfs order = [] start_nodes = [n for n in graph if node_to_indegree[n] = = 0 ] queue = collections.deque(start_nodes) while queue: node = queue.popleft() order.append(node) for neighbor in node.neighbors: node_to_indegree[neighbor] - = 1 if node_to_indegree[neighbor] = = 0 : queue.append(neighbor) return order def get_indegree( self , graph): node_to_indegree = {x: 0 for x in graph} for node in graph: for neighbor in node.neighbors: node_to_indegree[neighbor] + = 1 return node_to_indegree 算法流程 拓扑排序的算法是典型的宽度优先搜索算法,其大致流程如下: 统计所有点的入度,并初始化拓扑序列为空。 将所有入度为 0 的点,也就是那些没有任何依赖的点,放到宽度优先搜索的队列中 将队列中的点一个一个的释放出来,放到拓扑序列中,每次释放出某个点 A 的时候,就访问 A 的相邻点(所有A指向的点),并把这些点的入度减去 1 。 如果发现某个点的入度被减去 1 之后变成了 0 ,则放入队列中。 直到队列为空时,算法结束 一些实际案例: https: / / www.cnblogs.com / bonelee / p / 11724346.html |
外加一个djstra最短路算法:
下面示例求解最短的飞行航线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | from heapq import heappush, heappop def dijkstra(graph, from_node, to_node): q = [( 0 , from_node, [])] seen = {from_node} while q: cost, node, path = heappop(q) path = path + [node] if node = = to_node: return cost, path for adj_node, c in graph.get(node, {}).items(): if adj_node not in seen: seen.add(adj_node) heappush(q, (cost + c, adj_node, path)) return - 1 , [] air_lines = { "1" : { "2" : 2000 , "3" : 2000 , "4" : 4000 , "5" : 4500 }, "2" : { "5" : 1000 }, "3" : { "4" : 1000 }, "4" : { "5" : 500 }} print (dijkstra(air_lines, "1" , "4" )) print (dijkstra(air_lines, "1" , "5" )) print (dijkstra(air_lines, "4" , "5" )) print (dijkstra(air_lines, "5" , "4" )) print (dijkstra(air_lines, "1" , "1" )) print (dijkstra(air_lines, "10" , "10" )) |
==》上面代码有问题,下面这个才是ok的!因为1,4返回了4000的结果,其实是3000!理由是4再第一次加入队列以后再也没有机会更新了!实际上,134路径还应该有更新的机会:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from heapq import heappush, heappop def dijkstra(graph, from_node, to_node): q, seen = [( 0 , from_node, [])], set () while q: cost, node, path = heappop(q) seen.add(node) path = path + [node] if node = = to_node: return cost, path for adj_node, c in graph.get(node, {}).items(): if adj_node not in seen: heappush(q, (cost + c, adj_node, path)) return - 1 , [] air_lines = { "1" : { "2" : 2000 , "3" : 2000 , "4" : 4000 , "5" : 4500 }, "2" : { "5" : 1000 }, "3" : { "4" : 1000 }, "4" : { "5" : 500 }} print (dijkstra(air_lines, "1" , "4" )) print (dijkstra(air_lines, "1" , "5" )) print (dijkstra(air_lines, "4" , "5" )) print (dijkstra(air_lines, "5" , "4" )) print (dijkstra(air_lines, "1" , "1" )) print (dijkstra(air_lines, "10" , "10" )) |
发现bfs和dfs都搞不定的时候,大概率要用这个!
====》上面的模板有问题,因为没有最短距离的松弛,见文末的代码!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | from collections import defaultdict from heapq import heappush, heappop class Solution: def maxProbability( self , n: int , edges: List [ List [ int ]], succProb: List [ float ], start: int , end: int ) - > float : g = self .construct(edges, succProb) return self .dijkstra2(g, start, end) def construct( self , e, p): return [(e[i][ 0 ], e[i][ 1 ], p[i]) for i in range ( 0 , len (p))] def dijkstra2( self , edges, start_node, end_node): graph = defaultdict( dict ) for src, dst, distance in edges: graph[src][dst] = distance graph[dst][src] = distance q = [( - 1 , start_node)] found_min_dist_nodes = set () distances = {start_node: 0 } back_paths = {} while q: val, min_dist_node = heappop(q) cost = - val # redundant, because we never push a vertex (we've already seen) to the heap # if min_dist_node in found_min_dist_nodes: # continue found_min_dist_nodes.add(min_dist_node) if min_dist_node = = end_node: return cost for neibor_node, distance in graph[min_dist_node].items(): if neibor_node in found_min_dist_nodes: continue prev_dist = distances.get(neibor_node, 0 ) new_dist = cost * distance if new_dist > prev_dist: distances[neibor_node] = new_dist heappush(q, ( - new_dist, neibor_node)) return 0 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2017-10-23 递归神经网络——就是解决AST这样的问题
2017-10-23 循环神经网络
2017-10-23 卷积神经网络——本质上是在利用卷积做特征压缩,然后再全连接
2017-10-23 神经网络和反向传播算法——反向传播算法本质上是随机梯度下降,链式求导法则而来的
2017-10-23 LSTM入门学习——结合《LSTM模型》文章看
2017-10-23 LSTM入门学习——本质上就是比RNN的隐藏层公式稍微复杂了一点点而已
2017-10-23 LSTM模型