最小生成树

连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。

强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。

连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。

生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。

最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。

在这里插入图片描述

Kruskal算法

此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。

  1. 把图中的所有边按代价从小到大排序;
  2. 把图中的n个顶点看成独立的n棵树组成的森林;
  3. 按权值从小到大选择边,所选的边连接的两个顶点ui,vi应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
  4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。

在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;
const int N = 1e5+5;
int n, m, a, b, v;
struct Edge
{
	int fr, to, l;
};

bool cmp(Edge e1, Edge e2)
{
	return e1.l < e2.l;
}

Edge graph[N];
Edge tree[N];
int fa[N];

int find(int x)
{
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void merge(int x, int y)
{
	fa[find(x)] = find(y);
}

void Kruskal()
{
	for(int i = 1; i <= n; ++i)
		fa[i] = i;
	sort(graph, graph+m, cmp);
	for(int i = 0, j = 0; i < n-1 && j < m; ++j)
	{
		Edge curr = graph[j];
		if(find(curr.fr) != find(curr.to))
		{
			tree[i++] = curr;
			merge(curr.fr, curr.to);
		}
	}
}

int main()
{
	cin >> n >> m;
	for(int i = 0; i < m; ++i)
		cin >> graph[i].fr >> graph[i].to >> graph[i].l;
	Kruskal();
	for(int i = 0; i < n-1; ++i)
	{
		Edge e = tree[i];
		cout << e.fr << "->" << e.to << ":" << e.l << endl;
	}
	return 0;
}

Prim算法

输入:
6 10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 5 6
3 6 4
4 3 5
4 6 2
5 6 6

#include<iostream>
#include<string>
#include<vector>
#include<new>
using namespace std;

typedef long long ll;

struct Graph{
	int vexnum; //顶点个数 
	int edge;	//边的条数 
	int ** arc;	//邻接矩阵
	string *information; //记录每个顶点名称 
};

//创建图
void createGraph(Graph &g){
	
	cin >> g.vexnum >> g.edge; 
	
	g.information = new string[g.vexnum];
	g.arc = new int*[g.vexnum];
	int i = 0;
	
	//开辟空间的同时,进行名称的初始化
	for(i = 0; i < g.vexnum; ++i){
		g.arc[i] = new int[g.vexnum];
		g.information[i] = "v" + std::to_string(i+1); //对每个顶点进行命名
		for(int k = 0; k < g.vexnum; ++k){
			g.arc[i][k] = INT_MAX; //初始化邻接矩阵 
		} 		
	} 
	
	//cout << "请输入每条边之间的顶点编号(顶点编号从1开始),以及该边的权重:" << endl;
	for(i = 0; i < g.edge; ++i){
		int start;
		int end;
		cin >> start; //输入每条边的起点 
		cin >> end; //输入每条边的终点 
		int weight;
		cin >> weight;
		g.arc[start-1][end-1] = weight; //无向图的边是相反的 
	}
} 

//打印图
void print(Graph g){
	int i;
	for(i = 0; i < g.vexnum; ++i){
		cout << g.information[i] << " ";
		for(int j = 0; j < g.vexnum; ++j){
			if(g.arc[i][j] == INT_MAX)
				cout << "∞" << " ";
			else
				cout << g.arc[i][j] << " "; 
		}
		cout << endl;
	}
}

//作为记录边的信息
//这些边都是达到end的所有边中,权重最小的那个
struct Assis_array{
	int start; //边的起点 
	int end; //边的终点 
	int weight; //边的权重 
}; 

//进行prim算法实现,使用的邻接矩阵方法实现
void Prim(Graph g, int begin){
	//close_edge这个数组记录到达某个顶点的各个边中的权重最大的那个边
	Assis_array *close_edge = new Assis_array[g.vexnum];
	
	int j;
	
	//进行close_edge的初始化,更加开始起点进行初始化
	for(j = 0; j < g.vexnum; ++j){
		if(j != begin - 1){
			close_edge[j].start = begin - 1;
			close_edge[j].end = j;
			close_edge[j].weight = g.arc[begin - 1][j];
		}
	} 
	//把起点的close_edge中的值设置为-1,代表已经加入到集合U了
	close_edge[begin - 1].weight = -1;
	//访问剩下的顶点,并依次加入到集合U
	for(j = 1; j < g.vexnum; ++j){
		int min = INT_MAX;
		int k;
		int index;
		//寻找数组close_edge中权重最小的那个边
		for(k = 0; k < g.vexnum; ++k){
			if(close_edge[k].weight != -1){
				if(close_edge[k].weight < min){
					min = close_edge[k].weight;
					index = k;
				}
			}
		}
		//将权重最小的那条边的终点也加入到集合U
		close_edge[index].weight = -1;
		//输出对应的边的信息
		cout << g.information[close_edge[index].start]
			<< "-----"
			<< g.information[close_edge[index].end]
			<< "="
			<< g.arc[close_edge[index].start][close_edge[index].end]
			<< endl;
		//更新close_edge数组
		for(k = 0; k < g.vexnum; ++k){
			if(g.arc[close_edge[index].end][k] < close_edge[k].weight){
				close_edge[k].weight = g.arc[close_edge[index].end][k];
				close_edge[k].start = close_edge[index].end;
				close_edge[k].end = k;
			}
		} 
	} 
} 

int main()
{
	Graph g;
	createGraph(g);
	print(g);
	Prim(g, 1);
	return 0;
}

转载请注明出处:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值