并查集,最小生成树
并查集
字面意思就是几个集合的并集,和这个并集的查找。
百度百科:并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,
然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。
在博客上,看到一篇很生动的文章,关于并查集的。看了它,也就基本知道并查集的求法了https://blog.csdn.net/u013546077/article/details/64509038
最小生成树:
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。 [2
Prim算法:
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
我做这道题目的时候,虽然明白了prim算法,但是不知道增加最小的边,后来用了一个时间复杂度O(N3)的算法,就是在添加新边的时候,从已有的点从头到尾遍历,结果超时了。
后来知道可以用贪心的思想,很快就写出来了。
代码实现如何:
import java.util.Arrays; import java.util.Scanner; public class Main { static final int MAX = 105; static final int INF = 1000005; static int dis[][] = new int[MAX][MAX]; static int mid[] = new int[MAX]; static boolean vis[] = new boolean[MAX]; public static void main(String []args) { Scanner cin = new Scanner(System.in); while(true) { int N = cin.nextInt(); if(N == 0) { return; } else if(N == 1) { System.out.println(0); continue; } Arrays.fill(vis, false); int M = N*(N-1)/2; for(int i = 0; i < M; i++) { int a,b,c; a = cin.nextInt(); b = cin.nextInt(); c = cin.nextInt(); dis[a][b] = c; dis[b][a] = c; } System.out.println(Prim(N)); } } static int Prim(int N) { int sum = 0; mid[1] = 0; for(int i = 2; i <= N; i++) { mid[i] = dis[1][i]; if(dis[1][i] == 0) { mid[i] = INF; } } vis[1] = true; for(int i = 2; i <= N; i++) { int Min = INF; int temp = 0; for(int j = 2; j <= N; j++) { if(vis[j] == false && Min > mid[j]) { Min = mid[j]; temp = j; } } vis[temp] = true; sum += Min; for(int j = 2; j <= N; j++) { if(vis[j] == false && mid[temp]+dis[temp][j] < mid[j]) { mid[j] = mid[temp]+dis[temp][j]; } } } } }
kruskal算法的实现过程,有自己去实践了!