题目链接:https://www.luogu.com.cn/problem/P1111
题目类型典型的求最小生成树,可以使用prim算法或者kruskal算法,这里使用kruskal算法!
大致思路:
-
对所有边进行排序(权值升序);
-
边遍历所有边边用并查集(路径减半优化)去重已经挑选好的边;
-
结束条件:挑好的边等于村庄总个数,如果所有边遍历完表明没有最小生成树,则输出-1;
对于排序我们可以选择(手写快速排序)
import java.util.Scanner;
public class Main {
static class Edge {
int from;
int to;
int weight;
public Edge(int from, int to, int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
/**
* 并查集 - 路径减半优化
*/
static class UnionFind {
int parents[];
public UnionFind(int n) {
parents = new int[n];
for (int i = 1; i < n; i++) {
parents[i] = i;
}
}
int find(int v) {
while (v != parents[v]) {
parents[v] = parents[parents[v]];
v = parents[v];
}
return v;
}
}
static Edge[] edges;//所有边
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
UnionFind uf = new UnionFind(n + 1);
edges = new Edge[m];
for (int i = 0; i < m; i++) {
edges[i] = new Edge(in.nextInt(), in.nextInt(), in.nextInt());
}
quickSort(0, edges.length);
int nums = 0, ans = 0;
for (Edge edge : edges) {
int from = uf.find(edge.from);
int to = uf.find(edge.to);
if (from == to) continue;//判断是否已选择
ans = Math.max(ans, edge.weight);
nums++;
if (nums == n - 1) {
System.out.println(ans);
return;
}
//把to和from记为同个集合
uf.parents[from] = to;
}
System.out.println(-1);
}
private static void quickSort(int begin, int end) {
if (end - begin < 2) return;
int mid = pivotIndex(begin, end);
quickSort(begin, mid);
quickSort(mid + 1, end);
}
private static int pivotIndex(int begin, int end) {
//随机抽取一个当做轴点,并将其放在begin位置
swap(begin, begin + (int) ((Math.random() * (end - begin))));
//备份轴点
Edge t = edges[begin];
//将轴点放在合适位置
end--;//指向最后一个元素
while (begin < end) {
while ((begin < end)) {//先从右边开始
if (t.weight < edges[end].weight) {
end--;
} else {
edges[begin++] = edges[end];
break;//调换方向
}
}
while ((begin < end)) {
if (t.weight > edges[begin].weight) {
begin++;
} else {
edges[end--] = edges[begin];
break;//调换方向
}
}
}
//将轴点放入
edges[begin] = t;
return begin;
}
private static void swap(int i, int j) {
Edge t = edges[i];
edges[i] = edges[j];
edges[j] = t;
}
}
我们也可以使用手写小顶堆来排序
import java.util.Scanner;
public class Main {
static class Edge {
int from;
int to;
int weight;
public Edge(int from, int to, int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
/**
* 并查集 - 路径减半优化
*/
static class UnionFind {
int parents[];
public UnionFind(int n) {
parents = new int[n];
for (int i = 1; i < n; i++) {
parents[i] = i;
}
}
int find(int v) {
while (v != parents[v]) {
parents[v] = parents[parents[v]];
v = parents[v];
}
return v;
}
}
/**
* 小顶堆
*/
static class minHeap {
Edge elements[];
int size;
public minHeap(Edge[] heap) {
this.elements = heap;
size = heap.length;
hepfiy();
}
void hepfiy() {//自下而上的下滤
for (int i = (size >> 1) - 1; i >= 0; i--) {
siftDown(i);
}
}
private void siftDown(int index) {
Edge element = elements[index];
int half = size >> 1;
while (index < half) {//拥有子节点的节点
//两种情况:只有左子节点,左右都有
int childIndex = (index << 1) + 1;
Edge child = elements[childIndex];//左子节点
int rightChild = childIndex + 1;
if (rightChild < size && child.weight > elements[rightChild].weight) {//右节点比左节点大
childIndex = rightChild;
child = elements[rightChild];
}
if (child.weight >= element.weight) break;
//父节点小于子节点,交换
elements[index] = child;
index = childIndex;
}
elements[index] = element;
}
Edge poll() {
Edge first = elements[0];
elements[0] = elements[--size];
//下滤
siftDown(0);
return first;
}
public boolean isEmpty() {
return size == 0;
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
UnionFind uf = new UnionFind(n + 1);
Edge[] edges = new Edge[m];
for (int i = 0; i < m; i++) {
edges[i] = new Edge(in.nextInt(), in.nextInt(), in.nextInt());
}
minHeap queue = new minHeap(edges);
int nums = 0, ans = 0;
while (!queue.isEmpty()) {
Edge edge = queue.poll();
int from = uf.find(edge.from);
int to = uf.find(edge.to);
if (from == to) continue;//判断是否已选择
ans = Math.max(ans, edge.weight);
nums++;
if (nums == n - 1) {
System.out.println(ans);
return;
}
//把to和from联合
uf.parents[from] = to;
}
System.out.println(-1);
}
}