图论算法(7) --- 用并查集求最小生成树

6 篇文章 0 订阅

求最小生成树,最经典的算法无非是prim算法和kruskal算法,而时间效率上,kruskal更胜一筹。对于kruskal的实现,这里我们采用并查集的思想。


很简单,也就是说,我们先对边按权值从小到大排序,然后遍历每一条边。


遍历过程中的每一条边,我们判断边的两个节点是否是连通的:


(1)若连通,则跳过此边;


(2)若不连通,则此边一定为最小生成树上的一条边,将此边加入结果集;


当遍历完所有的边,结果集里的所有边即组成了这个无向图的最小生成树。


并查集的作用就是来判断边上的两点是否连通,对于并查集,如不了解,建议先学习其相关知识,非常简单,这里不做讲解。


下面附上代码,代码关键的地方带有注释,如不理解请留言。


package test;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;

public class MinSpanningTree {
	class Edge {//内部类定义边的数据结构
		int u, v, weight;
	}

	ArrayList<Edge> Edges = new ArrayList<Edge>();
	Map<Integer, Integer> nodeFather = new HashMap<Integer, Integer>();
	int cnt = 0, nodeCnt = 0;

	public MinSpanningTree(String path) {
		try {

			BufferedReader br = new BufferedReader(new FileReader(path));
			String str;
			String[] strArray;

			while ((str = br.readLine()) != null) {
				strArray = str.split("\\s");
				Edges.add(cnt, new Edge());
				Edges.get(cnt).u = Integer.parseInt(strArray[0]);
				Edges.get(cnt).v = Integer.parseInt(strArray[1]);
				Edges.get(cnt).weight = Integer.parseInt(strArray[2]);
				if (!nodeFather.containsKey(Edges.get(cnt).u)) {
					nodeFather.put(Edges.get(cnt).u, Edges.get(cnt).u);//初始化,father[i]=i;
					++nodeCnt;
				}
				if (!nodeFather.containsKey(Edges.get(cnt).v)) {
					nodeFather.put(Edges.get(cnt).v, Edges.get(cnt).v);
					++nodeCnt;
				}
				++cnt;
			}
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public boolean union(int u, int v) {//并操作
		int a = find(u);
		int b = find(v);
		if (a != b) {
			nodeFather.put(a, b);
			return true;
		}
		return false;
	}

	public int find(int x) {//查操作
		if (x != nodeFather.get(x)) {
			nodeFather.put(x, find(nodeFather.get(x)));
		}
		return nodeFather.get(x);
	}

	public ArrayList<Edge> getMinSpanningTree() {
		ArrayList<Edge> result = new ArrayList<Edge>();
		Queue<Edge> FsQueue = new PriorityQueue<Edge>(1000,//设置优先队列,使按边权值从小到大排序
				new Comparator<Edge>() {
					public int compare(Edge EdgeOne, Edge EdgeTwo) {
						if (EdgeOne.weight > EdgeTwo.weight)
							return 1;
						else if (EdgeOne.weight < EdgeTwo.weight)
							return -1;
						else
							return 0;
					}
				});

		for (int i = 0; i < cnt; ++i) {
			FsQueue.add(Edges.get(i));
		}

		while (!FsQueue.isEmpty()) {//遍历每一条边
			Edge Edge = FsQueue.poll();
			if (union(Edge.u, Edge.v)) {
				result.add(Edge);
			} else {
				continue;
			}
		}
		return result;
	}

	public static void main(String[] args) {
		MinSpanningTree mstree = new MinSpanningTree("c:/treedata.txt");
		ArrayList<Edge> result = mstree.getMinSpanningTree();
		for (int i = 0; i < result.size(); ++i) {
			System.out.println(result.get(i).u + " " + result.get(i).v + " "+ result.get(i).weight);
		}
	}
}

输入文件的格式为:

u v weight,即输入1 2 5,代表一条边,边的顶点是1和2,1和2之间的权值是5

输出格式亦是

u v weight,包含了最小生成树的所有边

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值