图 最短路径算法--普里姆算法与克里斯卡尔算法

<pre code_snippet_id="1933514" snippet_file_name="blog_20161017_1_459168" name="code" class="java">一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边。所谓的最小成本,就是n个顶点,
用n-1条边把一个连通图连接起来,并且使得权值的和最小。综合以上两个概念,我们可以得出:构造连通网的最小代价生成树,即最小生成树(Minimum Cost Spanning Tree)。
找连通图的最小生成树,经典的有两种算法,普里姆算法和克鲁斯卡尔算法首先咱们来说普里姆算法 
<span style="font-family: Arial, Helvetica, sans-serif;">先构造一个邻接矩阵 <img src="https://github.com/wangkuiwu/datastructs_and_algorithm/blob/master/pictures/graph/prim/01.jpg?raw=true" alt="" /></span><span style="font-family: Arial, Helvetica, sans-serif;">  </span><span style="font-family: Arial, Helvetica, sans-serif;"> </span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family:Arial, Helvetica, sans-serif;">以上图为例  从A点开始用普里姆算法构造一个最小生成树</span>
<span style="font-family:Arial, Helvetica, sans-serif;">
</span>
<span style="font-family:Arial, Helvetica, sans-serif;"><img src="https://github.com/wangkuiwu/datastructs_and_algorithm/blob/master/pictures/graph/prim/02.jpg?raw=true" alt="" />
</span>
<span style="font-family:Arial, Helvetica, sans-serif;">
</span>
<span style="font-family:Arial, Helvetica, sans-serif;"></span><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-size: 16px; color: rgb(51, 51, 51); word-wrap: break-word; word-break: normal; font-family: "Microsoft YaHei", Arial, 宋体;"><span style="color: rgb(0, 0, 0);"><strong>初始状态</strong>:V是所有顶点的集合,即V={A,B,C,D,E,F,G};U和T都是空! 
<strong>第1步</strong>:将顶点A加入到U中。 
     此时,U={A}。 
<strong>第2步</strong>:将顶点B加入到U中。 
     上一步操作之后,U={A}, V-U={B,C,D,E,F,G};因此,边(A,B)的权值最小。将顶点B添加到U中;此时,U={A,B}。 
<strong>第3步</strong>:将顶点F加入到U中。 
     上一步操作之后,U={A,B}, V-U={C,D,E,F,G};因此,边(B,F)的权值最小。将顶点F添加到U中;此时,U={A,B,F}。 
<strong>第4步</strong>:将顶点E加入到U中。 
     上一步操作之后,U={A,B,F}, V-U={C,D,E,G};因此,边(F,E)的权值最小。将顶点E添加到U中;此时,U={A,B,F,E}。 
<strong>第5步</strong>:将顶点D加入到U中。 
     上一步操作之后,U={A,B,F,E}, V-U={C,D,G};因此,边(E,D)的权值最小。将顶点D添加到U中;此时,U={A,B,F,E,D}。 
<strong>第6步</strong>:将顶点C加入到U中。 
     上一步操作之后,U={A,B,F,E,D}, V-U={C,G};因此,边(D,C)的权值最小。将顶点C添加到U中;此时,U={A,B,F,E,D,C}。 
<strong>第7步</strong>:将顶点G加入到U中。 
     上一步操作之后,U={A,B,F,E,D,C}, V-U={G};因此,边(F,G)的权值最小。将顶点G添加到U中;此时,U=V。  </span></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-size: 16px; color: rgb(51, 51, 51); word-wrap: break-word; word-break: normal; font-family: "Microsoft YaHei", Arial, 宋体;"><span style="color: rgb(0, 0, 0);">此时,最小生成树构造完成!它包括的顶点依次是:<strong>A B F E D C G</strong>。</span></p>
 
代码说明:
  class <span style="font-family: "Microsoft YaHei", Arial, 宋体; font-size: 16px;">MatrixUDG{</span>
    private char[] mVexs;       // 顶点集合
    private int[][] mMatrix;    // 邻接矩阵
    private static final int INF = Integer.MAX_VALUE;   // 最大值
    
    ...
}


MatrixUDG是邻接矩阵对应的结构体。mVexs用于保存顶点,mEdgNum用于保存边数,mMatrix则是用于保存矩阵信息的二维数组。例如,mMatrix[i][j]=1,则表示"顶点i(即mVexs[i])"和"顶点j(即mVexs[j])"是邻接点;mMatrix[i][j]=0,则表示它们不是邻接点。
/*
 * prim最小生成树
 *
 * 参数说明:
 *   start -- 从图中的第start个元素开始,生成最小树
 */
public void prim(int start) {
    int num = mVexs.length;         // 顶点个数
    int index=0;                    // prim最小树的索引,即prims数组的索引
    char[] prims  = new char[num];  // prim最小树的结果数组
    int[] weights = new int[num];   // 顶点间边的权值
    
    // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。
    prims[index++] = mVexs[start];
    
    // 初始化"顶点的权值数组",
    // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
    for (int i = 0; i < num; i++ )
        weights[i] = mMatrix[start][i];
    // 将第start个顶点的权值初始化为0。
    // 可以理解为"第start个顶点到它自身的距离为0"。
    weights[start] = 0;
    
    for (int i = 0; i < num; i++) {
        // 由于从start开始的,因此不需要再对第start个顶点进行处理。
        if(start == i)
            continue;
    
        int j = 0;
        int k = 0;
        int min = INF;
        // 在未被加入到最小生成树的顶点中,找出权值最小的顶点。
        while (j < num) {
            // 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。
            if (weights[j] != 0 && weights[j] < min) {
                min = weights[j];
                k = j;
            }
            j++;
        }
    
        // 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。
        // 将第k个顶点加入到最小生成树的结果数组中
        prims[index++] = mVexs[k];
        // 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。
        weights[k] = 0;
        // 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。
        for (j = 0 ; j < num; j++) {
            // 当第j个节点没有被处理,并且需要更新时才被更新。
            if (weights[j] != 0 && mMatrix[k][j] < weights[j])
                weights[j] = mMatrix[k][j];
        }
    }
    
    // 计算最小生成树的权值
    int sum = 0;
    for (int i = 1; i < index; i++) {
        int min = INF;
        // 获取prims[i]在mMatrix中的位置
        int n = getPosition(prims[i]);
        // 在vexs[0...i]中,找出到j的权值最小的顶点。
        for (int j = 0; j < i; j++) {
            int m = getPosition(prims[j]);
            if (mMatrix[m][n]<min)
                min = mMatrix[m][n];
        }
        sum += min;
    }
    // 打印最小生成树
    System.out.printf("PRIM(%c)=%d: ", mVexs[start], sum);
    for (int i = 0; i < index; i++)
        System.out.printf("%c ", prims[i]);
    System.out.printf("n");
}

而另一个算法则是采用的另一种思想  


克里斯卡尔算法


先构造一个连通图






克里斯卡尔算法重要是用连通图的边形成回环来判断

流程大致如下:

1.将无向图的边按距离长短递增式排序,放到集合中
2.遍历该集合,找出最短的边,加入到结果生成树的集合中
3.如果结果生成树出现回路,则放弃这条边
4.重新执行步骤2,直至所有顶点被遍历
可以看出在每次遍历过程中采用了贪心算法



实体


  1. /*  
  2.  * 克鲁斯卡尔->最小生成树边实体  
  3.  * */  
  4. public class Edge {  
  5.       
  6.    private int begin;  
  7.    private int end;  
  8.    private int weight;  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值