[图论]最小生成树之Prim算法与Kruskal算法

最小生成树

生成树

对某一图,从图中任一顶点出发,遍历图,遍历所经过的边以及所有顶点构成一颗生成树。

因此,对于有n个顶点的图,其生成树有n个顶点,n-1条边。

边权之和称为代价,代价最小的生成树即为最小生成树。

最小生成树不一定唯一,带它们的权值相等。

通常,使用Prim算法以及Kruskal算法来求取最小生成树。

Prim算法

顶点出发考虑。

原有顶点集V,边集E;建立最小生成树顶点集U,边集TE,初始U为空。

  1. (初始化)在V中任选一顶点 ,加入U.
  2. V − U ≠ 0 V-U\neq 0 VU̸=0,在U、V - U 中各选一个顶点,使得该边权值最小,将该边加入TE,V - U 中顶点加入U.
  3. 重复(2.).

代码实现

const int MAX = 9999999;
const int NUM = 105;
vector<vector<int> > adjtable(NUM, vector<int>(NUM, MAX));

int Prim(vector<vector<int> > adj, int num){
	//输入邻接矩阵adj以及顶点数num 
	int result = 0;
	int* lowcost = new int[NUM];
	int* visited = new int[NUM];
	for(int i = 0;i<num;i++){
		lowcost[i] = adj[0][i];	// 建立lowcost列表,由顶点0开始,初始化 
		visited[i] = 0;			// 建立visited列表,初始化为0表示未访问 
	}
	visited[0] = 1;	// 从0开始,标记为1表示已访问 
	// 遍历各顶点 
	for(int i = 0;i<num;i++){
		int mini = MAX;	// 待选取得最小边权,初始化为MAX 
		int pos = 0;	// 最小边权对应的在V-U中的顶点下标,即尚未加入生成树的顶点下标 
		for(int j = 0;j<num;j++){
			if(!visited[j] && mini > lowcost[j]){
				// 当顶点j未被访问,那么判断 <v, j> 边权是不是当前最小边权,
                // v为已加入生成树中的某一顶点 
				mini = lowcost[j];
				pos = j;
			}
		}
        // 当mini未被更新,表示所有顶点已经加入生成树,完成构建
		if(mini == MAX) break; 
		result += mini;
		visited[pos] = 1; // pos标记的顶点加入生成树 
		// 更新lowcost,
        // 若当前加入生成树的顶点与未加入顶点之间的距离小于lowcost中的值,则更新 
		for(int j = 0;j<num;j++){
			if(!visited[j] && lowcost[j] > adj[pos][j]){
				lowcost[j] = adj[pos][j];
			}
		}
	}
	return result;
}

Kruskal算法

出发考虑。

原有顶点集V,边集E;建立最小生成树顶点集U,边集TE,初始U为空。

  1. 若E未被读取完,在E中选权值最小的一条边,判断该边两顶点是否都在TE中;

    若E被读取完,则完成算法.

  2. 若都在TE中,跳过该边,读取E中除此边以外最小的边,重复(1.);

    若不都在TE中,将该边加入TE,重复(1.).

代码实现

const int NUM = 105;
struct edges{
	int v1, v2;
	int val;
	edges(int a, int b, int c): v1(a), v2(b), val(c){}
};

vector<vector<int> > adjtable(NUM, vector<int>(NUM, 0));
vector<edges> Edges;
int visited[NUM];
int T_pre[NUM];

int find(int x){
	int r = x;
	while(T_pre[r] != r){
		r = T_pre[r];
	}
	int i = x, j;
	while(i!=r){
		j = T_pre[i];
		T_pre[i] = r;
		i = j;
	}
	return r;
}

int join(int x, int y){
	int a = find(x);
	int b = find(y);
	if(a!=b) T_pre[a] = b;
}

bool cmp(edges e1, edges e2){
	return e1.val < e2.val;
}

int Kruskal(vector<vector<int> > adj, int num){
	int result = 0;
	for(int i = 0;i<num;i++){
		// 初始化并查集 T_pre, T_pre记录所有已加入生成树的顶点
		T_pre[i] = i;
		for(int j = 0;j<num;j++){
			// 初始化边集 
			if((!visited[i]||!visited[j]) && adj[i][j]){
				edges e(i, j, adj[i][j]);
				Edges.push_back(e);
			}
		}
	}
	// 边集中,按照边权排序 
	sort(Edges.begin(), Edges.end(), cmp);
	int a, b; 
	for(int i = 0;i<Edges.size();i++){
		a = find(Edges[i].v1);
		b = find(Edges[i].v2);
		// a!=b,即二者不在同一集合(生成树顶点集)中,将该边加入生成树 
		if(a!=b){
			result += Edges[i].val;
			join(Edges[i].v1, Edges[i].v2);
		}
	}
	return result;
}

// Szp 2018/12/10

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值