【算法导论04】贪心算法-kruskal算法

04贪心算法-kruskal算法

问题描述:
给定一个无向图,设计算法找出其中的最小生成树。

思路分析:
kruskal算法属于贪心算法,因此满足贪心算法的本质:局部最优解一定是全局最优解。在最小生成树中的应用即为,一个无向图中包含最小权值边点两个点,一定在最小生成树中,因此,只需要对该无项图中的顶点进行排序,按顺序找出边权值最小的顶点即可。但这个问题实际包含了两个问题:
1、如何对边进行排序?
2、如果新加入的边使得该最小生成树形成回路,如何解决?
针对 问题1:对无向图的各个边进行从小到大的堆排序(堆排序在此处的应用时间复杂度最低)
针对 问题2:将无向图的边加入并查集,通过并查集来判断是否构成环路,如果构成环路,即跳过该边。
(注:由于本文着重讲解kruskal算法,堆排序部分便不再赘述,下方算法也直接调用该函数)

算法分析:
假设无向图的边已经按照从小到大顺序排好,则此时kruskal算法的内核就是并查集的使用。kruskal算法步骤如下:
1、初始化一个parent数组,用于构造一棵树,数组下标为根序号,数组值为顶点序号,如parent[3]=4表示顶点4的根为3。
2、按照边权值由小到大的顺序,依次扫描边,并找出该边中顶点的根,如果新加入的边的顶点的根与已经加入边的顶点的根不相等,则将两个边进行合并,即将新的边加入最小生成树;如果新加入的边的顶点的根与已经加入顶点的根相等,则构成环路,舍弃该边,继续执行。
3、循环上述步骤。

笔记:(-from MIT算法导论课程)
在这里插入图片描述

kruskal算法代码:

#include<stdio.h>
#include<stdlib.h>

#define vertices 6

//对并查集进行初始化,所有值初始化为-1
void initialise(int parent[],rank[]){
	int i;
	for(i=0;i<vertices;i++){
		parent[i] = -1;
		rank[i] = 0;
	}
	
}

//找根结点,用于连接和判断
int find_root(int x,int parent[]){
	int x_root = x;
	if(x_root!=-1){
		x_root = parent[x_root];
	}
	return x_root;
}

//并查集主体,找到根后,判断根是否相同,如果相同,则有环,如果不同,则进行连接
void union_vertices(int x,int y,int parent[],rank[]){
	//当根不相等时:谁的根高,谁就做父结点。
		if(rank[x_root] > rank[y_root]){
			parent[y_root] = x_root;
		}else if(rank[y_root] > rank[x_root]){
			parent[x_root] = y_root;
		}else{
			parent[x_root] = y_root;
			rank[y_root]++;
		}
}

//结构体定义边的两个顶点和权重
typedef struct edge{
	int a,b;
	int weight;
}
//kruskal算法
void MST_kruskal(graph g,edge *edges,int parent[],int rank[]){
	
	//对边权重进行堆排序
	heap_sort(edges);
	//对边进行循环,找边的两个顶点的根,如果根不相等,则进行连接
	for(int i=0;i<vertices;i++){ 
		int a_root=find_root(edge[i].a,parent);
		int b_root= find_root(edge[i].b,parent);
	if(a_root!=b_root)
		union_vertices(a_root,b_root,parent,rank);
	}
}

int main(){
	
	//	无向图的初始化
	int parent[vertices] = {0};
	int rank[vertices];
	for(int i=0;i<6;i++){
		scanf(%d%d%d”,&edge[i].a,&edge[i].b,edge[i].weight);
	}
	initialise(parent,rank);		
	MST_kruskal(g,edges,parent,rank);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值