数据结构——最小支撑树


前言

树:同时满足极小连通图与极大无环图
支撑树:能够覆盖图中每一个顶点的树
最小支撑树:有权网络中满足各边权值之和最小的支撑树

对于一幅图来说,存在的最小支撑树并不唯一。


一、Prim算法(由小树长成大树)

(1)思路

最小支撑树总是会采用联结每一割的最短跨越边。
用我的理解,就是不断拓展树的大小,用贪心思想在目前的最小支撑树的邻接点中搜索与树相连的边的最小权值,并将其加入树,之后更新树的邻接点的数据,直到树能够覆盖所有的顶点

适用于稠密图,O(|V|^2)

(2)代码实现

#include<bits/stdc++.h>
#define MAX 0x3f3f3f;
using namespace std;
int sum;
int n,m;//输入的N个点,和M条边
int x,y,z;//输入变量
int i,j,k;//循环变量
int book[100];//用于记录这个点有没有被访问过
int dis[100];//用于记录距离树的距离最短路程
int maps[100][100];//用于记录所有边的关系
int Prim(){
	int min,minIndex;
	//初始化距离数组,默认先把离1点最近的找出来放好
    for (i = 1; i <= n; i++)
        dis[i] = maps[1][i];
 
    book[1]=1;//记录1已经被访问过了
 
    for (i = 1; i <= n-1; i++){//1已经访问过了,所以循环n-1次
        min = MAX;//对于最小值赋值
        for (j = 1; j <= n; j++){//搜索树的邻接点与树相连的边的权最小值及该顶点 
            if(!book[j]&& dis[j] < min)
            {
                min = dis[j];
                minIndex = j;
            }
        }
 		//记录这个点已经被访问过了
        book[minIndex] = 1;
        sum += dis[minIndex];
 
        for (j = 1; j <= n; j++){
            //刚刚树增加了一个点,现在把与这个点相连的边的数据更新为更小的权值
			//通过这一步骤,我们可以在dis里面存入树的邻接点与树相连的边的权值 
            if(book[j] == 0 && maps[minIndex][j] < dis[j])
                dis[j] = maps[minIndex][j];
        }
    }
}
int main(){    
    cin>>n>>m;
//----------------------------------------------------
    //初始化maps,除了自己到自己是0其他都是边界值
    for (i = 1; i <= n; i++){
        for (j = 1; j <= n; j++){
            if(i!=j){
            		maps[i][j] = MAX; 
			}
            else	maps[i][j] = 0;
        }
    }
    for (i = 1; i <= m; i++){
        cin>>x>>y>>z;//输入的为无向图
        maps[x][y] = z;
        maps[y][x] = z;
    }
//----------------------------------------------------
    
     cout<<Prim()<<endl;
}

二、kruskal算法(合木成林)

(1)思路

贪心思想,每次会在剩余的边中选出权值最小的边,从而构成一棵棵子树,最终合木成林,但对于边的选择却存在要求,边不能属于同一棵子树,否则就形成了环路,不符合树的要求,直至边数等于总顶点数减一,结束检索。

适用于稀疏图,O(|V|*log|V|)

(2)代码实现

#include <bits/stdc++.h>
using namespace std;
struct Edge{
    int u;
    int v;
    int w;
};
int parent[201];
//---------------------------------------- 
bool cmp(Edge e1, Edge e2){//对于结构体内部数据排序所需要 
	return e1.w < e2.w; 
}
//---------------------------------------- 
int find(int x) {//递归求子树最终根节点 
    if (x == parent[x])
        return x;
    return parent[x] = find(parent[x]);//更新每个点的最终根节点 
}
//---------------------------------------- 
int merge(int x, int y){//判断是否属于同一棵子树 
    int a = find(x);
    int b = find(y);
    if (a != b) {
        parent[b] = a;
        return 1;
    }
    return 0;
}
int main(){
    int n, e;
    while (cin >> n >> e){
        Edge edge[e];
        for (int i = 0; i <e; i++){//进行初始化 
        parent[i] = i;//先保证不同 
    	}
        for (int i = 0; i < e; i++){
            cin>>edge[i].u>>edge[i].v>>edge[i].w;
        }
        sort(edge,edge+e,cmp);
        int cnt = 0, sum = 0;//cnt为树中的边数,sum为树中各边之和 
        for (int i = 0; i < e; i++){
            if (merge(edge[i].u, edge[i].v)){
                cnt++;
                sum += edge[i].w;
            }
            if (cnt == n - 1)
                break;
        }
        if (cnt == n - 1)
            cout << sum << endl;
        else
            cout <<"NO TREE"<< endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值