图最短路径和最小生成树

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)

 

posted @ 2019-04-25 10:24  LeeJuly  阅读(212)  评论(0)    收藏  举报