最小生成树
普里姆(Prim)算法
找出与当前生成树相邻的最小的边(不会构成回路:因为是只有生成树内部点相连才会构成回路,而算法选择的是当前生成树到为并入点的最小距离)
克鲁斯卡尔(Kruskal)算法
直接选取权值最小的边(不构成回路:并查集)
并查集
Dijkstra算法成立的前提条件是不存在负权的边
所以 最短路径在不存在负边的前提下,有"渔网"的物理解法(真的是现实世界的降维打击)
Dijkstra算法:计算某个源点到其他点的最短距离
与当前的点集相邻的最短距离
对普里姆(Prim)算法稍加改动既是Dijkstra 算法,(二者十分类似,只不过在求最短距离时prim面对的是所有的点集,而Dijstra面对的是源点v0,所以有了距离逐步累加的过程)
源点到任意一个点的最短距离,要么是源点到它的某条边的长度,要么是源点到另一个点的最短距离距离,再加上另一个点到这个点的边的长度
已经并入的节点 | 未并入 | dist[] | path[] | 备注 |
---|---|---|---|---|
vo | 未并入 | {+,100,50,10*,+} | {-1,0,0,0,-1} | 并入v3 |
v0,v3(并入v3后) | 未并入 | {+,100,50*,10,90} | {-1,0,0,0,3} | 并入v2 |
v0,v3,v2 | 未并入 | {+,100,50,10,90*} | {-1,0,0,0,3} | 并入v4 |
v0,v3,v2,v4 | 未并入 | {+,100*,50,10,90} | {-1,0,0,0,3} | 并入v1 |
v0,v3,v2,v4,v1 | 未并入 | {+,100*,50,10,90} | {-1,0,0,0,3} | 并入v1 |
dijstra算法的贪心为什么是正确的?
Dijkstra算法的贪心策略是基于局部最优解,即从起点到当前节点的最短路径,然后通过不断更新节点的距离来求出整个图的最短路径。这种贪心策略的正确性基于以下两个事实:
-
局部最优解是全局最优解的一个组成部分:在Dijkstra算法中,每次都选择距离起点最近的节点作为下一个处理的节点,这个节点的距离就是从起点到该节点的最短距离,因此这个局部最优解一定是全局最优解的一个组成部分。
-
没有更优的路径可以到达某个节点:一旦一个节点的距离被确定,就不可能有更短的路径可以到达该节点,因为如果存在更短的路径,那么这个节点的距离就会被更新成更短的距离,这个事实也保证了局部最优解一定是全局最优解的一个组成部分。
因此,Dijkstra算法的贪心策略是正确的。
Floyd算法:计算每一对顶点间的最短距离
function floyd(arr,path) {
let i,j,v;
for(v=0;v<n;v++)/*对于某个中枢节点*/
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if(arr[i][j]>arr[i][v]+arr[v][j]){arr[i][j]=arr[i][v]+arr[v][j];path[i][j]=v;}
}
var path=[[-1,-1,-1,-1],[-1,-1,-1,-1],[-1,-1,-1,-1],[-1,-1,-1,-1]];
var a=[[0,5,1000,7],[1000,0,4,2],[3,3,0,2],[1000,1000,1,0]];
floyd(a,path);
console.log(a);
console.log(path);
function printpath(qi,zhi,path) {
if(path[qi][zhi]==-1){console.log("链接"+qi+"-->"+zhi);}
else
{
var mid=path[qi][zhi];//中间点
printpath(qi,mid,path);//分而治之
printpath(mid,zhi,path);//分而治之
}
}
printpath(1,2,path);