萌新学习算法——并查集进阶(最小生成树)
上面我们通过对并查集的了解,学会了如何判断是两个元素是否在同一个集合,接下来,我们还是通过洛谷的题目来进行解析:lP3366 最小生成树.
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz
输入格式
第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)
接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi
输出格式
输出包含一个数,即最小生成树的各边的长度之和;如果该图不连通则输出orz。
首先要得出最小生成树的方法通常有两种:Prim算法和Kruskal算法两种,在这里我们用的是Kruskal算法来实现的。
思想:
Keuskal的基本思想(贪心策略):先将权值按照从小到大的顺序排列,将每一条边试图加入集合中,若不会生成环,则加入*(判断方法利用并查集的方法来判断来个点是否在同一个集合里面,若在同一个则证明有环,不加入,否则加入)*
首先通过类来包装树的节点Node。
代码实现类Node
class Node {
int start, end, number;
//有三个参数的构造函数
public Node(int start, int end, int number) {
this.start = start;
this.end = end;
this.number = number;
}
}
接着利用Comparator接口来实现排序sortNode
class SortNode implements Comparator<Node> {
public int compare(Node n1, Node n2) {
//n1.number-n2.number,则返回1,表示从小到大排序
//n2.number-n1.number,反之从大到小
return n1.number - n2.number;
}
}
find函数:
public static int find(int x)
{
if(data[x]<0) return x;
return data[x]=find(data[x]);
}
完整代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
class Node {
int start, end, number;
//有三个参数的构造函数
public Node(int start, int end, int number) {
this.start = start;
this.end = end;
this.number = number;
}
}
class SortNode implements Comparator<Node> {
public int compare(Node n1, Node n2) {
return n1.number - n2.number;
}
}
public class Main {
public static int data[];
public static int find(int x)
{
if(data[x]<0) return x;
return data[x]=find(data[x]);
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String str[] = reader.readLine().split(" ");
int n = Integer.parseInt(str[0]), m = Integer.parseInt(str[1]);
Node node[] = new Node[m];
for (int i = 0; i < m; i++) {
str = reader.readLine().split(" ");
node[i] = new Node(Integer.parseInt(str[0]), Integer.parseInt(str[1]), Integer.parseInt(str[2]));
}
if (n == 1) {
System.out.println(node[0].number);
return;
}
Arrays.sort(node, new SortNode()); //进行权值排序
long sum = 0; //统计最小值
data=new int[n+1];
for(int i=1;i<=n;i++) //初始化并查集数组
data[i]=-1;
int bian=0; //统计边数
for (int i = 0; i < m; i++) {
int s = node[i].start, e = node[i].end;
int x=find(s),y=find(e);
if(x==y) //产生环,舍弃
continue;
sum+=node[i].number; //
bian+=1; //边数+1
if(bian==n-1) //利用最小连通图节点数=边数-1的概念,判断退出条件
break;
data[x]+=data[y]; //更新根节点的数据
data[y]=x; //更新y的根节点为x
}
if (bian == n - 1)
System.out.println(sum);
else
System.out.println("orz");
}
}