最小生成树——Prim算法和Kruskal算法

本文介绍两种求解最小生成树的经典算法:Prim算法和Kruskal算法。Prim算法从某个顶点出发逐步构建树,每次选择离当前树最近的顶点加入;Kruskal算法则按边的权重从小到大排序,依次加入不会形成环的边。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最小生成树——Prim算法和Kruskal算法

一、prim算法解析
在这里插入图片描述
以上图为例。

prim算法从任意顶点开始,每次都找到一个距离顶点集中距离最近的一个点,将其加入到顶点集,并把两点间的边加入到树中。直到访问完所有点,得到的就是最小生成树。
步骤:
1、从点A开始,将其加入到顶点集中,此时涉及的边有两条,[A,B]和[A, C],显然可以判断出后者距离更小,所以把C点加入到顶点集中。
在这里插入图片描述
在这里插入图片描述

2、在此顶点集中,涉及的边数增加,在所有边中继续寻找顶点集中没有的距离最短的点。这里可以找到第三个点为F。以此类推,可以访问到所有点。
注意
1、不可以构成回路。
2、遇到权值相同的任选一个点,对结果没影响。

二、Kruskal算法
上面的prim算法是从点的角度下手,而Kruskal算法是从边下手。
在这里插入图片描述
以上图为例

步骤:
1、首先将边按小到大的顺序排序。实例中排序可得,(1, 4), (2, 3),(0, 1), (1, 4),(1, 2),(2, 4),(3, 4)。
2、从小到大取出边及连接边的起始点和终止点,将取出的点和边做记号放入同一个集合中。直 到所有的点存入同一个集合并没有形成回路。如下图演示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意:其中当选取到边<1, 4>时,因为0, 1, 4已经在同一集合中,选取边<1, 4>后会造成回路,所以直接到下一条边<1, 2>。

三、完整代码

#include<iostream>
using namespace std;


//prim算法
#define X 10000
const int N = 6;
int edge[N] = {0};
bool visit[N];
int graph[N][N] = { { X, 3, 1, X, X, X},  //INF代表两点之间不可达
                    { 3, X, 6, 4, 7, X},
                    { 1, 6, X, 5, X, 1},
                    { X, 4, 5, X, 6, 8},
                    { X, 7, X, 6, X, 2},
                    { X, X, 1, 8, 2, X}
                  };

int prim(int spot){

    int index = spot;
    int i = 0;
    int j = 0;
    int sum = 0;
    memset(visit,false, sizeof(visit));
    visit[spot] = true;

    cout << "最小生成树: ";
    cout << index << " ";
    for(i = 0; i < N; i++){
        edge[i] = graph[index][i];
    }
    for(i = 1; i < N; i++){

        int value = X;

        for(j = 0; j < N; j++){//找到未访问过的最小权值的点
            if(!visit[j] && edge[j] < value){
                value = edge[j];
                index = j;
            }
        }

        visit[index] = true;//标记刚刚找到的点
        cout << index << " ";
        sum += edge[index];// 累加权值

        for(j = 0; j < N; j++){//更新数组edge的值
            if(!visit[j] && edge[j] > graph[index][j]){
                edge[j] = graph[index][j];
            }
        }
    }

    cout << endl;
    cout << "最短距离: ";
    return sum;
}


//kruskal算法
#define N 7
typedef struct node{
    int val;//权值
    int start;//起始点
    int end;//终止点
}Node;
Node V[N];

int Edge[N][3] = {  { 0, 4, 1},
                    { 2, 3, 2},
                    { 0, 1, 4},
                    { 1, 4, 4},
                    { 1, 2, 5},
                    { 2, 4, 6},
                    { 3, 4, 7}
                    };
 
int father[N] = {0};
int cap[N] = {0};
 
void make_set()              //初始化集合,让所有的点都各成一个集合,每个集合都只包含自己
{
    for(int i = 0; i < N; i++)
    {
        father[i] = i;//各成一个集合
        cap[i] = 1;//集合中结点个数
    }
}
 
int find_set(int x)              //找出点属于哪个集合
{
    if(x != father[x])
     {
        father[x] = find_set(father[x]);//运用递归找出点的“祖集合”
    }
    return father[x];
}
 
void Union(int x, int y)         //将x,y两点合并到同一个集合
{
    x = find_set(x);
    y = find_set(y);
    if(x == y)
        return;
    if(cap[x] < cap[y])
        father[x] = find_set(y);
    else
    {
        cap[x] += cap[y];
        father[y] = find_set(x);
    }
}
 
int Kruskal()
{
    int sum = 0;
    make_set();//初始化集合
    for(int i = 0; i < N; i++)//将边从小到大排序后,依次取出
    {
        if(find_set(V[i].start) != find_set(V[i].end))     //如果改变的两个顶点还不在一个集合中,就并到一个集合里,生成树的长度加上这条边的长度
        {
            Union(V[i].start, V[i].end);  //合并两个顶点到一个集合
            sum += V[i].val;
        }
    }
    return sum;
}
int main(){
    cout << "prim算法结果: " << endl;
    cout << prim(0) << endl;
    
    cout << "Kruskal算法结果: " << endl;
    for(int i = 0; i < N; i++)   //初始化边的数据
    {
        V[i].start = Edge[i][0];
        V[i].end = Edge[i][1];
        V[i].val = Edge[i][2];
    }
    cout << "最小生成树权值: ";
    cout << Kruskal()<< endl;
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值