最小生成树的建立——Prim算法

最小生成树的建立

基本概念

要构建最小生成树之前要理解与图相关的基本术语和概念:

  • 树图

    任意两个顶点之间只有一条路径的无向连通图称为树图,n个顶点恰好有n-1条边。

  • 连通图

    假设在无向图G中,任意两个顶点都是连通的,则称图G是连通图

  • 生成树的概念

    假设在连通图G中有n个顶点,链接n个顶点的n-1条边构成无回路的连通子图称为生成树,满足生成树的可能又会多种,所以生成树不唯一。

  • 最小生成树的概念

    由于图的生成树不是唯一的,在一个图中可能存在多个生成树,将个边权值相加之和最小的生成树, 称为最小生成树。

  • 稀疏图

    稀疏图,顾名思义,这个图看起来结构简单不怎么复杂呗~ 其实就是 满足边或者弧的个数e <= n l o g n nlog_{}n nlogn(n代表图中顶点的个数)

  • 稠密图

    既然有稀疏图,那么也就有于之相反的概念稠密图, 稠密图满足边或弧的个数e > n l o g n nlog_{}n nlogn

Prim算法

思路:Prim算法将顶点分为两部分,U和V-U,U中的顶点代表当前生成最小生成树的顶点,V—U就是没有处理过的顶点,起始时将全部的顶点之间的边作为候选边,依次从中选取权值最小的边作为最小生成树的边,不断修正U,直到得到构成最小生成树全部的顶点。

prim实现的过程

设置关键数组
  • 设置lowcost数组来存储边的权值,如果lowcost[i]=0表示 表名i就在U中,已经找构成最小生成树的点。
  • closevertex数组表示构成最小生成树相邻的顶点,理解当前构成最小生成的前驱,这个具体在运行的结果中看到,一会在看到输出结果是详细解释。
实现最小生成树的图解

以下图为例 以V0为起始点图中为了方便观看省去了V:

在这里插入图片描述
初始化两个数组有:

在这里插入图片描述

  1. 第一次进行构建:
    在这里插入图片描述
    对应数组的值为:
    在这里插入图片描述
  2. 第二次进行构建:

在这里插入图片描述
对应数组的值为:

在这里插入图片描述

在数组中选择到的最小值为1,其实就是刚刚走过的路径,所以这次相当于将之前的走过的路径标记为已经走过

  1. 第三次进行构建:
    在这里插入图片描述
    对应数组的值为:
    在这里插入图片描述
  2. 第四次进行构建:
    在这里插入图片描述
    对应数组的值为:
    在这里插入图片描述
  3. 第五次进行构建:
    在这里插入图片描述
    对应数组的值为:
    在这里插入图片描述
  4. 第六次进行构建:
    在这里插入图片描述
    对应数组的值为:
    在这里插入图片描述
根据图解给出Prim算法的实现过程

代码如下:

void Prim(Graph G, int v){
    int k, lowcost[MAX_VEX], closevertex[MAX_VEX];
    for(int i = 0; i < G.numvex; i++){
        lowcost[i] = G.arc[v][i];			  //初始化 将第v行的权值依次纳入其中,含义就是v到其他顶点的权值
        closevertex[i] = v;				   //保存相邻结点的信息,可以理解为下一个构成最小生成树结点的前驱
    }
    for(int i = 0; i < G.numvex; i++){
        int min = INF;
        for(int j = 0 ;j < G.numvex; j++ ){
            if(lowcost[j] < min && lowcost[j] != 0){
                min = lowcost[j];			     // 不断更新最小值,直到它是当前的最小值 
                k = j;                 			 // 将找到的最小权值的下标赋值给k
            }
        }
        printf("arc from V%d to V%d wight : %d\n", closevertex[k], k, lowcost[k]);
        //当前顶点的权值设置为0,表示此顶点已经找到最小生成树的顶点,进行下一个顶点的查找
        lowcost[k] = 0;											
	        for(int j = 0; j < G.numvex; j++){
	        //如果当前v到目标点的路径 有比刚刚最小值点到目标点大的话,修正最小值
	            if(lowcost[j] != 0 && lowcost[j] > G.arc[k][j]){    
	                lowcost[j] = G.arc[k][j];
	                // 找到与构成最小生成树顶点相邻的顶点信息,其实也就是前驱的顶点
	                closevertex[j] = k; 					       
	            }
	        }
	        cout << endl;
	    }
	}
完整版实现过程

代码如下:

#include <iostream>
#include <cstdio>
#define MAX_VEX 100
#define INF 65535
using namespace std;
struct Graph{
    char vexs[MAX_VEX];
    int arc[MAX_VEX][MAX_VEX];
    int numvex,numarc;
};
void CreateGraph(Graph &G){
    int vi, vj, w;
    cout << "please enter the number of vertexes and arcs : \n";
    cin >> G.numvex >> G.numarc;
    for(int i = 0; i < G.numvex; i++){
        printf("Please enter the NO.%d name of vex : ",i+1);
        cin >> G.vexs[i];
    }

    for(int i = 0; i < G.numvex; i++){
        for(int j = 0; j < G.numvex ;j++){
            G.arc[i][j] = INF;
        }
    }
    cout << endl;
    for(int i = 0; i < G.numarc; i++){
        cout<< "Enter the subscripts and weights from vertex vi to vertex vj : ";
        cin >> vi >> vj >> w;
        G.arc[vi][vj] = w;
        G.arc[vj][vi] = w;
    }
}
void DispalyGraph(Graph G){
    for(int i = 0; i < G.numvex; i++) cout << G.vexs[i] << " ";
    cout << endl;
    for(int i = 0; i < G.numvex; i++){
        for(int j = 0; j < G.numvex; j++){
            if(G.arc[i][j] == INF) printf("%6s", "∞");
            else printf("%6d", G.arc[i][j]);
        }
        cout << endl;
    }
}
void Prim(Graph G, int v){
    int k, lowcost[MAX_VEX], closevertex[MAX_VEX];
    for(int i = 0; i < G.numvex; i++){
        lowcost[i] = G.arc[v][i];
        closevertex[i] = v;
    }
    for(int i = 0; i < G.numvex; i++){
        int min = INF;
        for(int j = 0 ;j < G.numvex; j++ ){
            if(lowcost[j] < min && lowcost[j] != 0){
                min = lowcost[j];
                k = j;
            }
        }
        printf("arc from V%d to V%d wight : %d\n", closevertex[k], k, lowcost[k]);
        lowcost[k] = 0;
        for(int j = 0; j < G.numvex; j++){
            if(lowcost[j] != 0 && lowcost[j] > G.arc[k][j]){
                lowcost[j] = G.arc[k][j];
                closevertex[j] = k;
            }
        }
        cout << endl;
    }
    for(int i = 0; i < G.numvex; i++) cout << closevertex[i]<< " ";
}
int main(){
    Graph G;
    CreateGraph(G);
    DispalyGraph(G);
    Prim(G,0);
    return 0;
}
	/*
	0 1 6
	0 2 1
	0 3 5
	1 2 5
	2 3 5
	1 4 3
	4 2 6
	2 5 4
	5 3 2
	4 5 6*/
实现结果

在这里插入图片描述
解释:

观察执行的过程,比较容易理解 这里每一行输出的信息,依次为 closevertex[k], k, lowcost[k]。含义就是 从V0-v2 构成的边的权值为,第二次是V2-v0,这样正好和第二次探索的数组 1 5 0 5 6 4 符合,将lowcost[0]置为0 得到 0 5 0 5 6 4 由于并没有比当前lowcost 中权值小的,所以当前lowcost 数组值不做任何变化。继续探索 过程就是 V2-V5 权值为 4 ,V5-V3 权值为2, V2-v1 权值为5, V1-V4 权值为3 。这样就构成了最小生成树。通过closevertex 来去保存k的值,k就是此次找到构成最小最小生成树的顶点,而输出时的closevertex 输出的是上一次保存k的值。所以这就是说closevertex是保存当前构成最小生成树的前驱结点啦~ 理解这个很重要,它类似与最短路径 Dijkstra算法十分相似,他们的思想本质也是一样的,用一个数组来保存前驱,区别就是加上判断条件完成找到时不改变信息,完成构建时就是最短路径的下标,这个在Dijkstra算法中详细说到。

分析

而Prim算法是根据点开始找边的,于图中的边数无关,所以Prim算法更适合用于稠密图,不需要考虑边的复杂性, Prim算法的时间复杂度为O( n 2 n^{2} n2 )。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值