数据结构 最小生成树 Prim算法

数据结构 最小生成树 Prim算法

Prim算法(普里姆算法)
求加权连通图的最小生成树的算法,从一个点开始不断让树长大的过程, 并且保持权值最小:
从任意一个顶点开始,每次选择一个与当前顶点集最近的一个顶点,并将两顶点之间的边加入到树中。Prim算法在找当前最近顶点时使用到了贪婪算法

基本思想
1、对于图G而言,V是所有顶点的集合,设置两个新的集合U和T,
U存放G的最小生成树中的顶点,
T存放G的最小生成树中的边。
2、从所有uЄU,vЄ(V-U) (V-U表示出去U的所有顶点)的边中选取权值最小的边(u, v),将顶点v加入集合U中,将边(u, v)加入集合T中,如此不断重复,直到U=V为止,最小生成树构造完毕,这时集合T中包含了最小生成树中的所有边

算法描述:
1. 在一个加权连通图中,顶点集合V,边集合为E
2. 任意选出一个点作为初始顶点,标记为visit,计算所有与之相连接的点的距离,选择距离最短的,标记visit.
3. 重复以下操作,直到所有点都被标记为visit:
在剩下的点中,计算与已标记visit点距离最小的点(计算的是未标记点距离所有标记的点的最近距离),标记visit,证明加入了最小生成树

性能分析
具体性能也是取决于优先队列的选择,一般来说可以达到 O(ElogV)

时间复杂度
邻接矩阵:O(v^2)
邻接表:O(elog2v)

import java.io.IOException;
import java.util.Scanner;

public class Prim {
    private char[] mVexs;       // 顶点集合
    private int[][] mMatrix;    // 邻接矩阵
    private static final int INF = 100;//Integer.MAX_VALUE;   // 最大值

    //创建图(用已提供的矩阵)
    public Prim(char[] vexs, int[][] matrix) {// vexs-- 顶点数组 ,matrix-- 矩阵(数据)        
        // 初始化"顶点数"和"边数"
        int vlen = vexs.length;
        // 初始化"顶点"
        mVexs = new char[vlen];
        for (int i = 0; i < mVexs.length; i++)
            mVexs[i] = vexs[i];
        // 初始化"边"
        mMatrix = new int[vlen][vlen];
        for (int i = 0; i < vlen; i++)
            for (int j = 0; j < vlen; j++)
                mMatrix[i][j] = matrix[i][j];
    }

    //返回ch位置
    private int getPosition(char ch) {
        for(int i=0; i<mVexs.length; i++)
            if(mVexs[i]==ch)
                return i;
        return -1;
    }

    //返回顶点v的第一个邻接顶点的索引,失败则返回-1
    private int firstVertex(int v) {
        if (v<0 || v>(mVexs.length-1))
            return -1;
        for (int i = 0; i < mVexs.length; i++)
            if (mMatrix[v][i]!=0 && mMatrix[v][i]!=INF)
                return i;
        return -1;
    }

    //打印邻接顶点的索引、权重
    private void nextVertexs() {
        for(int v = 0; v < mVexs.length; v++){
            System.out.print("\n结点"+v);
            for (int i = 0; i < mVexs.length; i++) {
               if (mMatrix[v][i]!=0 && mMatrix[v][i]!=INF)
                  System.out.print("邻接点"+i+"(权重"+mMatrix[v][i]+")");
            }
        } 
    }

    //打印矩阵队列图
    public void print() {
        int i, j, sum;
        System.out.printf("结点表:");
        for (i=0; i < mVexs.length; i++) { 
            sum = 0;
            for (j=0; j < mVexs.length; j++) { 
               if (mMatrix[i][j]!=0 && mMatrix[i][j]!=INF) {
                  sum++;
               }
            }
            System.out.print("\n结点"+i+", 标识"+mVexs[i]+", 共有邻接点"+sum);
        }

        System.out.print("\n\n");
        System.out.printf("邻接矩阵:\n");
        for (i = 0; i < mVexs.length; i++) {
            for (j = 0; j < mVexs.length; j++)
                System.out.printf("%3d ", mMatrix[i][j]);
            System.out.printf("\n");
        }

        nextVertexs();
    }

    /*
     * 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];   // 顶点间边的权值

        prims[index++] = mVexs[start]; // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。

        // 初始化"顶点的权值数组",将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
        for (int i = 0; i < num; i++ )
            weights[i] = mMatrix[start][i];       
        weights[start] = 0; // 将第start个顶点的权值初始化为0, 可以理解为"第start个顶点到它自身的距离为0"。

        for (int i = 0; i < num; i++) { 
            if(start == i) // 由于从start开始的,因此不需要再对第start个顶点进行处理。
                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;  
            int n = getPosition(prims[i]); // 获取prims[i]在mMatrix中的位置      
            for (int j = 0; j < i; j++) {
                int m = getPosition(prims[j]);
                if (mMatrix[m][n]<min)  // 在vexs[0...i]中,找出到j的权值最小的顶点。
                    min = mMatrix[m][n];
            }
            sum += min;
        }

        // 打印最小生成树
        System.out.printf("\nPRIM(%c)=%d: ", mVexs[start], sum);
        for (int i = 0; i < index; i++)
            System.out.printf("%c \n", prims[i]);
        System.out.printf("\n");
    }

    public static void main(String[] args) {
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int matrix[][] = {
                 /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
          /*A*/ {   0,  12, INF, INF, INF,  16,  14},
          /*B*/ {  12,   0,  10, INF, INF,   7, INF},
          /*C*/ { INF,  10,   0,   3,   5,   6, INF},
          /*D*/ { INF, INF,   3,   0,   4, INF, INF},
          /*E*/ { INF, INF,   5,   4,   0,   2,   8},
          /*F*/ {  16,   7,   6, INF,   2,   0,   9},
          /*G*/ {  14, INF, INF, INF,   8,   9,   0}};
        Prim pG;
        pG = new Prim(vexs, matrix);

        pG.print(); // 输出图的有关信息  

        pG.prim(0);   // prim算法生成最小生成树
    }
}

程序输出:

结点表:
结点0, 标识A, 共有邻接点3
结点1, 标识B, 共有邻接点3
结点2, 标识C, 共有邻接点4
结点3, 标识D, 共有邻接点2
结点4, 标识E, 共有邻接点4
结点5, 标识F, 共有邻接点5
结点6, 标识G, 共有邻接点3

邻接矩阵:
0 12 100 100 100 16 14
12 0 10 100 100 7 100
100 10 0 3 5 6 100
100 100 3 0 4 100 100
100 100 5 4 0 2 8
16 7 6 100 2 0 9
14 100 100 100 8 9 0

结点0邻接点1(权重12)邻接点5(权重16)邻接点6(权重14)
结点1邻接点0(权重12)邻接点2(权重10)邻接点5(权重7)
结点2邻接点1(权重10)邻接点3(权重3)邻接点4(权重5)邻接点5(权重6)
结点3邻接点2(权重3)邻接点4(权重4)
结点4邻接点2(权重5)邻接点3(权重4)邻接点5(权重2)邻接点6(权重8)
结点5邻接点0(权重16)邻接点1(权重7)邻接点2(权重6)邻接点4(权重2)邻接点6(权重9)
结点6邻接点0(权重14)邻接点4(权重8)邻接点5(权重9)

PRIM(A)=36: A
B
F
E
D
C
G

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值