C/C++ 最小生成树—Kruskal算法

一、最小生成树

  • 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
  • 在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边,而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集且为无循环图,使得
    的 w(T) 最小,则此 T 为 G 的最小生成树。
  • 最小生成树其实是最小权重生成树的简称。

二、Kruskal算法(克鲁斯卡尔算法)

构造过程

假设连通网N = (V, E),将N中的边按权值从小到大的顺序排列。

  • 初始状态为只有n个顶点而无边的非连通图T = (V, {}),图中每个顶点自成一个连通分量。
  • 在E中选择权值最小的边,若改变依附的顶点落在T中不同的连通分量上(即不形成回路),则将此边加入到T中,否则舍去此边而选择下一条权值最小的边。
  • 重复第二步,直至T中所有顶点都在同一连通分量上为止。

算法步骤

  • 将数组Edge中的元素按权值从小到大排序。
  • 依次查看数组Edge中的边,循环执行以下操作:
    • 依次取出数组Edge中的一条边(U1, U2);
    • 用并查集数组vset找到U1、U2的父节点;
    • 如果父节点不等,则修改U2的父节点为U1的父节点,记录权值和,记录已合并的边数;
    • 当已记录的边数和等于 顶点数-1 时,退出循环;

四、举一个栗子(洛谷P3366)

Description

给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

Input

  • 第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边;
  • 接下来 M 行每行包含三个整数 Xi,Yi,Zi,表示有一条长度为 Zi的无向边连接结点 Xi 、Yi;

Sample Input

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

Sample Output

7

More Info

数据规模:
对于 20% 的数据,N≤5,M≤20;
对于 40% 的数据,N≤50,M≤2500;
对于 70% 的数据,N≤500,M≤104;
对于 100% 的数据:1≤N≤5000,1≤M≤2×105;

Code

详见注释

#include 
#include 
#define Max 200003

using namespace std;

typedef struct Edge {					//定义边的结构体
	int head, tail;
	int lowcost;
};

typedef struct Graph {					//定义图
	int vex, arc;
	Edge edge[Max];
};
int vset[Max], sum=0, n=0;				//并查集数组、总权值、最小生成树边数

bool cmp(Edge x, Edge y)				//自定义排序规则
{
	return x.lowcost < y.lowcost;
}

int find(int x)							//找到父亲结点
{
	if (vset[x] == x) return x;
	else return find(vset[x]);
}

void Kruskal(Graph &G)					//克鲁斯卡尔算法
{
	sort(G.edge, G.edge+G.arc, cmp);	//STL快排模板

	for (int i = 0; i < G.arc; i++)		//遍历边
	{
		int x, y;
		x = find(G.edge[i].head);		
		y = find(G.edge[i].tail);		//找到顶点的父亲节点
		if (x != y) {					//如果不在一个连通图里
			//cout << G.edge[i].head << " -> " << G.edge[i].tail << endl;
			sum += G.edge[i].lowcost;
			vset[y] = x;				//并入连通串
			if (++n == G.vex - 1) break;//如果满了n-1条边,则结束循环
		}
	}
	cout << sum;						//输出总权
}

void putin(Graph &G)					//输入函数
{
	cin >> G.vex >> G.arc;
	for (int i = 0; i < G.arc; i++)
		cin >> G.edge[i].head >> G.edge[i].tail >> G.edge[i].lowcost;
	for (int i = 1; i <= G.vex; i++)	//初始化并查集数组
		vset[i] = i;
}

int main()
{
	Graph G;
	putin(G);
	Kruskal(G);
	return 0;
}

蒟蒻一只,欢迎指正

你可能感兴趣的:(数据结构)