学习图相关的算法(Java 实现)(2)——Prim算法求最小生成树

学习图相关的算法(Java 实现)(2)——Prim算法求最小生成树


也不知道为啥要写博客。。。

学习图相关的算法(Java 实现)(1)

相关定义

由于太饿了,相关定义(加权图、生成树、最小生成树)已经被吃掉了,所以麻烦您自行搜索一下哈(~ ̄▽ ̄)~
下面就直奔主题

最小生成树

先来看一个简单的情况,两个节点的最小生成树(最小生成树定义允许存在权重为负的边),毫无疑问要选权重为-1的边。
在这里插入图片描述
接下来换一张节点多一些的图:
在这里插入图片描述
看到桌子上出现了一些头发?不要着急,你的好友小明已经帮你将左边三个节点 {1, 5, 6} (粉色节点)组成的子图和右边三个节点 {2, 3, 4} (黄色节点)组成的子图分别找到了最小生成树,你只需要将它们连接成一棵最小生成树
在这里插入图片描述

不妨将左边子图的最小生成树和右边子图的最小生成树分别看做一个节点
在这里插入图片描述
显然,应该选择权重为-1的边加入最小生成树。

Prim算法

假设目前已经有左边子图的最小生成树了,如下图
在这里插入图片描述
接下来,便可以在连接左边子图与右边节点的所有边中,选择权重最小的一条边,将其加入左边的最小生成树中,同时,该边连接的节点也加入了最小生成树,显然还是权重为-1的边(怎么总是它?),如下图
在这里插入图片描述
那么接下来就是如何用算法实现了:

  1. 数据结构

    -节点:boolean数组marked[]表示每个节点是否在最小生成树中
    -边:队列mst 保存最小生成树中的边
    -优先队列:MinPQ 根据权重比较边

  2. 算法

    1. 将节点0加入最小生成树中(marked[0]标记为true),与节点0相邻的所有边加入优先队列MinPQ
    2. 从优先队列MinPQ中找到权重最小且与该边相邻的一个节点不在最小生成树中的边
    3. 将该边加入最小生成树mst,并标记该边相邻的未被标记的节点
    4. 将该节点相邻的所有与该边相邻的一个节点不在最小生成树中的边加入优先队列
    5. 重复2, 3, 4直到所有节点被标记
// 代码直接照着《算法4》敲的(这样真的好吗?)
import java.util.*;

public class Prim {

	private boolean[] marked;
	private Queue<Edge> mst;
	private PriorityQueue<Edge> pq;
	
	public Prim(EdgeWeightedGraph G) {
		mst = new LinkedList<>();
		pq = new PriorityQueue<>();
		marked = new boolean[G.V()];
		visit(G, 0);
		while(!pq.isEmpty()) {
			Edge e = pq.poll();
			int v = e.either(), w = e.other(v);
			if(marked[v] && marked[w]) {
				continue;
			}
			mst.add(e);
			if(!marked[v]) {
				visit(G, v);
			}
			else {
				visit(G, w);
			}
		}
	}
	
	private void visit(EdgeWeightedGraph G, int v) {
		marked[v] = true;
		for(Edge e: G.adj(v))
			if(!marked[e.other(v)]) {
				pq.add(e);
			}
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}

class Edge implements Comparable{

	private final int v; // 顶点1
	private final int w; // 顶点2
	private final double weight; // 边的权重
	
	
	
	public Edge(int v, int w, double weight) {
		super();
		this.v = v;
		this.w = w;
		this.weight = weight;
	}

	public double weight() {
		return weight;
	}
	public int either() {
		return v;
	}
	public int other(int vertex) {
		return v == vertex? w: v;
	}
	@Override
	public int compareTo(Object o) {
		Edge that = (Edge)o;
		if(this.weight() < that.weight())
			return -1;
		if(this.weight() > that.weight())
			return 1;
		return 0;
	}
	
	public String toString() {
		return String.format("%d-%d %.2f", v, w, weight);
	}
	
}

class EdgeWeightedGraph {
	private final int V;
	private int E;
	private LinkedList<Edge>[] adj;
	public EdgeWeightedGraph(int V) {
		this.V = V;
		this.E = 0;
		adj = new LinkedList[V];
		for(int i = 0; i < V; ++i) {
			adj[i] = new LinkedList<Edge>();
		}
	}
	public int V() {
		return V;
	}
	public int E() {
		return E;
	}
	public void addEdge(Edge e) {
		int v = e.either(), w = e.other(v);
		adj[v].add(e);
		adj[w].add(e);
		E++;
	}
	public LinkedList<Edge> adj(int v){
		return adj[v];
	}
	public LinkedList<Edge> edges(){
		LinkedList<Edge> list = new LinkedList<Edge>();
		for(int v = 0; v < V; ++v) {
			for(Edge e: adj[v]) {
				if(e.other(v) > v) {
					list.add(e);
				}
			}
		}
		return list;
	}
}

Kruskal算法

反正应该也不会有人看,所以咕咕咕~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值