赞
踩
探索迷宫而不迷路的一种古老办法叫做Tremaux搜索(标记法),要探索迷宫中的所有通道,我们需要:
绳子可以保证你总能找到一条出路,标记则能保证你不会两次经过同一条通道或者同一个路口。
和Tremaux搜索类似,深度优先搜索,只需用一个递归方法来遍历所有顶点。在访问其中一个顶点时:
问题是这样:给定一幅图和一个起点s,回答“从s到给定目的顶点v是否存在一条路径?如果有,找出其中最短的那条(所含边数最少)。”
广度优先搜索是这样的:要找到从s到v的最短路径,从s开始,在所有由一条边就可以到达的顶点中寻找v,如果找不到我们就继续在与s距离两条边的所有顶点中查找v,如此一直进行。
在程序视线中,我们一般使用一个队列来保存所有已经被标记过但其邻接表还未被检查过的顶点。
广度优先搜索正是为了寻找迷宫的最短路径才出现的,因为:
图的生成树是它的一棵含有其所有顶点的无环连通子图。
一幅加权图的最小生成树(MST)是它的一棵权值(树中所有边的权值之和)最小的生成树。
最小生成树算法在设计各种类型的网络中都起到了重要的作用。
在一幅加权图中,给定任意的切分,它的横切边中的权重最小者必然属于图的最小生成树。
令e为权重最小的横切边,T为图的最小生成树。我们采用反证法:
切分定理是解决最小生成树问题的所有算法的基础。
使用切分定理找到最小生成树的一条边,不断重复直到找到最小生成树的所有边。
如何才能(有效地)找到最小权重的横切边呢?Prim算法使用一条优先队列MinPQ<Edge>来根据权重比较所有边。
每当我们向树中添加了一条边之后,也向树中添加了一个顶点。要维护一个包含所有横切边的集合,就要将连接这个顶点和其他所有不在树中的顶点的边加入优先队列。
class Solution: def minCostConnectPoints(self, points: List[List[int]]) -> int: from queue import PriorityQueue cal = lambda p1, p2: abs(p1[0] - p2[0]) + abs(p1[1] - p2[1]) # 计算曼哈顿距离的函数 pq = PriorityQueue() visit = set(range(len(points))) # 待访问的节点集 res = 0 pq.put((0, 0)) # (distance, point_id) while visit: # 当没有访问完所有节点 dis, now = pq.get() # 获取优先队列中最小的项 => (到扩展集中某最近点的距离,某最近点的序号) if now not in visit: # 已访问过的直接跳过 continue visit.remove(now) # 移除出待访问的节点集 res += dis for i in visit: # 构建扩展集,就是把当前点对所有未访问点的距离都求一遍 # 以距离为cost丢进优先队列排序 pq.put((cal(points[now], points[i]), i)) return res
定义:在一幅加权有向图中,从顶点s到顶点t的最短路径是所有从s到t的路径中的权重最小者。
松弛这个术语来自于用一根橡皮筋沿着连接两个顶点的路径紧紧展开的比喻:放松一条边就类似于将橡皮筋转移到一条更短的路径上,从而缓解了橡皮筋的压力。
边的松弛:放松边v→w意味着检查从s到w的最短路径是否是先从s到v,然后再由v到w。
顶点的松弛:谨慎选择顶点,使得每次顶点松弛操作都能得出到达某个顶点的更短的路径,最后逐渐找出到达每个顶点的最短路径。
判断路径是否为最短路径的全局条件与在放松一条边时所检测的局部条件是等价的。
必要性证明:
假设distTo[w]是从s到w的最短路径。如果对于某条从v到w的边e有distTo[w]>distTo[v]+e.weight(),那么从s到w(经过v)且经过e的路径的长度必然小于distTo[w],矛盾。因此最优性条件是必要的。
将distTo[s]初始化为0,其他distTo[]元素初始化为无穷大,继续如下操作:
对于任意从s可达的顶点w,在进行这些操作之后,distTo[w]的值即为从s到w的最短路径的长度。
通用算法并没有指定边的放松顺序。
Dijkstra算法主要特点是从起始点开始,每次都会为最短路径树添加一条顶点,这点距离始点距离最近且未被访问过,直到扩展到终点为止。
要实现Dijkstra算法,需要一条优先队列,以保存需要被放松的顶点并确认下一个被放松的顶点。
public void dijkstra(int s, int t) { // 从顶点s到顶点t的最短路径 int[] predecessor = new int[this.v]; // 用来还原最短路径 Vertex[] vertexes = new Vertex[this.v]; for (int i = 0; i < this.v; ++i) { vertexes[i] = new Vertex(i, Integer.MAX_VALUE); } PriorityQueue queue = new PriorityQueue(this.v);// 小顶堆 boolean[] inqueue = new boolean[this.v]; // 标记是否进入过队列 vertexes[s].dist = 0; queue.add(vertexes[s]); inqueue[s] = true; while (!queue.isEmpty()) { Vertex minVertex= queue.poll(); // 取堆顶元素并删除 if (minVertex.id == t) break; // 最短路径产生了 for (int i = 0; i < adj[minVertex.id].size(); ++i) { Edge e = adj[minVertex.id].get(i); // 取出一条minVetex相连的边 Vertex nextVertex = vertexes[e.tid]; // minVertex-->nextVertex if (minVertex.dist + e.w < nextVertex.dist) { // 更新next的dist nextVertex.dist = minVertex.dist + e.w; predecessor[nextVertex.id] = minVertex.id; if (inqueue[nextVertex.id] == true) { queue.update(nextVertex); // 更新队列中的dist值 } else { queue.add(nextVertex); inqueue[nextVertex.id] = true; } } } } // 输出最短路径 System.out.print(s); print(s, t, predecessor); } private void print(int s, int t, int[] predecessor) { if (s == t) return; print(s, predecessor[t], predecessor); System.out.print("->" + t); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。