图最短路径和最小生成树
1.最短路径问题:
Dijkstra算法:
从源节点到末尾节点,
S[]数组保存已经是最短路径的节点,
1.每次循环先找不在S[]中的节点的距离最小值节点u,这个节点一定会被保存到S中去
2.再更新其他节点到源节点的最短距离,通过这个最小节点u
再循环执行1,2
O(n^2)
public int[] Dijsktra(int[][] weight,int start){ int length = weight.length; int[] shortPath = new int[length];//存放从start到各个点的最短距离 shortPath[0] = 0;//start到他本身的距离最短为0 int visited[] = new int[length];//标记当前该顶点的最短路径是否已经求出,1表示已经求出 visited[0] = 1;//start点的最短距离已经求出 for(int count = 1;count<length;count++){ int k=-1; int dmin = Integer.MAX_VALUE; for(int i=0;i<length;i++){ if(visited[i]==0 && weight[start][i]<dmin){ dmin = weight[start][i];//首先获取距离start最近的一个节点 k=i; } } //选出一个距离start最近的未标记的顶点 将新选出的顶点标记为以求出最短路径,且到start的最短路径为dmin。 shortPath[k] = dmin;//这个节点到start的距离一定是最短的,所以现在更新 visited[k] = 1; //以k为中间点,修正从start到未访问各点的距离 for(int i=0;i<length;i++){ if(visited[i]==0 && weight[start][k]+weight[k][i]<weight[start][i]){ weight[start][i] = weight[start][k]+weight[k][i]; } } } return shortPath; }
Floyd算法:
通过每一个节点,更新任意两点间的最小距离(不断的更新一个二维数组,更新n次)
时间复杂度O(n^3)
public void floyd(int[][] mMatrix) { int n=mMatrix.length; int[][] path=new int[n][n];//path[i][j]用来保存i到j之间距离j节点最近的那个节点,主要是为了方便遍历的 int[][] dist=new int[n][n];//用来保存点之间最短距离,需要不断更新的 // 初始化 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { dist[i][j] = mMatrix[i][j]; // "顶点i"到"顶点j"的路径长度为"i到j的权值"。 if(i!=j && mMatrix[i][j]<Integer.MAX_VALUE) path[i][j] = i; // "顶点i"到"顶点j"的最短路径是经过顶点j。 else path[i][j] = -1; //这两个顶点之间不通 } } // 计算最短路径 for (int k = 0; k < n; k++) {//经过的节点 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (dist[i][j] > dist[i][k]+dist[k][j]) { dist[i][j] = dist[i][k]+dist[k][j]; path[i][j] = path[k][j]; } } } } }
2.一个带权重的无向图的最小生成树算法(连接n个节点的n-1条边切没有回路的子图称为生成树)
Prim算法:
用一个U集合保存已经在这颗最小生成树里面的节点(我们需要时刻把这个集合当做一个整体):
1.首先查找距离for循环源点最小的距离的节点,将它加入集合
2.通过加入的节点更新其他节点到集合的最短距离
再去循环1,2
因为整个算法过程只与节点个数有关,与边无关,所以很适合稠密图,n个节点,时间复杂度O(n^2)
public void Prim(int[][] mMatrix,int v) {//v是源节点 int n=mMatrix.length; int[] lowcost=new int[n];//lowcost[i]从源点v到节点i之间的距离 int[] closest=new int[n];//closest[i]源点v到节点i之间距离i最近的节点 for(int i=0;i<n;i++) { lowcost[i]=mMatrix[v][i]; closest[i]=v; } int k=0; //找剩下的n-1个节点 for(int i=1;i<n;i++) { int min=Integer.MAX_VALUE; for(int j=0;j<n;j++) { if(lowcost[j]!=0 && lowcost[j]<min) { min=lowcost[j]; k=j; } } lowcost[k]=0;//相当于把k加入到这个集合了,所以距离源点的距离就是0 //通过k来更新到这个集合最近的距离 for(int j=0;j<n;j++) { if(mMatrix[k][j]!=0&&mMatrix[k][j]<lowcost[j]) { lowcost[j]=mMatrix[k][j]; closest[j]=k; } } } }
Kruskal算法:
将图所有边的权重排序,依次按从小到大顺序从中选取边,如果选取的边没有形成回路的话,把他加入进去,直到n-1条边全部加完了。
关于加边容易出现回路的问题,我们是用一个集合标志,连接在一棵树上的节点它们的集合标志都是相同的,在加边的过程中,如果一条边的两个节点的集合标志是一样的话,就说明它们是同一棵树,那么这条边就不能加进去。
主要和边的个数有关,所以时间复杂度O(e^2),主要的时间花在排序上面,如果选用其他的几种方式排序,可以降到O(e*loge)
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/10766767.html

浙公网安备 33010602011771号