最小生成树算法模板 Java实现

Prim算法和Kruskal算法 Java实现

问题描述

在一个带权连通图图中找出一颗最小生成树,求其权值之和。

Prim算法

public class Prim {
	public static int inf=65535;
	public static int minval=0;
	
	public static void prim(int [][]map,int n) {
		int min,i,j,k;
		int visit[]=new int[n];//0表示未选择 ;1表示已选择
		int lowcost[]=new int[n];//表示顶点i到已选择的顶点集中的最短边的权值  若i在顶点集中,则为inf
		//首先选择点0
		lowcost[0]=inf;
		visit[0]=1;
		for(i=1;i<n;i++)
		{
			lowcost[i]=map[0][i];
			visit[i]=0;
		}
		for(i=1;i<n;i++)//循环n-1次,选择剩下的n-1个点
		{
			min=inf;
			k=0;
			//寻找连接已选择的顶点集到未选择的顶点集的最小权值边
			for(j=1;j<n;j++)
			{
				if(visit[j]==0&&lowcost[j]<min)
				{
					min=lowcost[j];
					k=j;
				}
			}
			//选择顶点k,记录总权值
			minval+=min;
			lowcost[k]=inf;
			visit[k]=1;
			//更新lowcost
			for(j=1;j<n;j++)
			{
				if(visit[j]==0&&map[k][j]<lowcost[j])
				{
					lowcost[j]=map[k][j];
				}
			}
		}
	}
	public static void main(String[] args) {
		 int map[][]= {{0,10,inf,inf,inf,11,inf,inf,inf},
					   {10,0,18,inf,inf,inf,16,inf,12},
					   {inf,inf,0,22,inf,inf,inf,inf,8},
					   {inf,inf,22,0,20,inf,inf,16,21},
					   {inf,inf,inf,20,0,26,inf,7,inf},
					   {11,inf,inf,inf,26,0,17,inf,inf},
					   {inf,16,inf,inf,inf,17,0,19,inf},
					   {inf,inf,inf,16,7,inf,19,0,inf},
					   {inf,12,8,21,inf,inf,inf,inf,0},
				 		};
		 prim(map,map.length);
		 System.out.print("最小生成树的权值为:"+minval);
	}
}
  • 时间复杂度: O ( n 2 ) O(n^{2}) O(n2)
  • 可使用二叉堆优化为: O ( e l o g 2 n ) O(elog_2n) O(elog2n)
  • 适用于稠密图

Kruskal算法

import java.util.*;
class Edge{
	int begin;
	int end;
	int weight;
	public int getWeight()
	{
		return weight;
	}
}
public class Kruskal {
	public static int inf=65535;
	public static int minval=0;
	public static void kruskal(int map[][]) {
		int i,j,m,n;
		int parent[]=new int[map.length];
		//读入所有边,并按权值从小到大排序
		List<Edge>edges=new ArrayList<Edge>();//定义边集数组
		for(i=0;i<map.length;i++)
		{
			for(j=0;j<map.length;j++)
			{
				if(map[i][j]!=inf&&i!=j)
				{
					Edge edge=new Edge();
					edge.begin=i;
					edge.end=j;
					edge.weight=map[i][j];
					edges.add(edge);
				}
			}
		}
		//按权由大到小排序
		edges.sort(Comparator.comparingInt(Edge::getWeight));
		//初始化并查集
		for(i=0;i<map.length;i++)
		{
			parent[i]=i;
		}
		//循环所有边
		for(i=0;i<edges.size();i++)
		{
			n=find(parent,edges.get(i).begin);
			m=find(parent,edges.get(i).end);
			
			if(n!=m)//n与m不相等,说明此边没有与现有的生成树形成环路
			{
				//将此边的结尾顶点放入下标为起点的parent中,表示此顶点已经在生成树集合中
				parent[n]=m;
				minval+=edges.get(i).weight;
			}
		}
	}
	//查找连线顶点的尾部下标
	public static int find(int parent[],int f)
	{
		while(parent[f]!=f)
		{
			f=parent[f];
		}
		return f;
	}
	public static void main(String[] args) {
		 int map[][]= {{0,10,inf,inf,inf,11,inf,inf,inf},
				   {10,0,18,inf,inf,inf,16,inf,12},
				   {inf,inf,0,22,inf,inf,inf,inf,8},
				   {inf,inf,22,0,20,inf,inf,16,21},
				   {inf,inf,inf,20,0,26,inf,7,inf},
				   {11,inf,inf,inf,26,0,17,inf,inf},
				   {inf,16,inf,inf,inf,17,0,19,inf},
				   {inf,inf,inf,16,7,inf,19,0,inf},
				   {inf,12,8,21,inf,inf,inf,inf,0},
			 		};
	 kruskal(map);
	 System.out.print("最小生成树的权值为:"+minval);
	}
}

  • 时间复杂度: O ( e log ⁡ 2 e ) O(e\log_2e) O(elog2e)
  • 适用于稀疏图(边少)

参考资料:《大话数据结构》

Java代码如下: ``` import java.util.*; class Edge implements Comparable<Edge>{ int u; // 起点 int v; // 终点 int w; // 权重 public Edge(int u, int v, int w) { this.u = u; this.v = v; this.w = w; } @Override public int compareTo(Edge o) { return Integer.compare(this.w, o.w); // 按照权重从小到大排序 } } public class Kruskal { static int MAXN = 10005; // 最大顶点数 static int[] father = new int[MAXN]; // 并查集数组 static PriorityQueue<Edge> pq = new PriorityQueue<Edge>(); // 优先队列 public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); // 顶点数 int m = sc.nextInt(); // 边数 // 初始化并查集 for(int i = 1; i <= n; i++) { father[i] = i; } // 读入边 for(int i = 1; i <= m; i++) { int u = sc.nextInt(); int v = sc.nextInt(); int w = sc.nextInt(); pq.offer(new Edge(u, v, w)); // 将边加入优先队列 } int res = 0; // 最小生成树的权值和 // Kruskal算法 while(!pq.isEmpty()) { Edge e = pq.poll(); // 取出当前最小的边 int fu = find(e.u); int fv = find(e.v); if(fu != fv) { // 如果两个顶点不在同一个集合中,则加入最小生成树 father[fu] = fv; // 合并两个集合 res += e.w; } } System.out.println(res); // 输出最小生成树的权值和 } // 并查集查找操作 static int find(int x) { if(father[x] == x) { return x; } else { return father[x] = find(father[x]); } } } ``` 其中,`Edge`类表示边,包括起点、终点和权重。`pq`是优先队列,用于存储边并按照权重从小到大排序。`father`数组是并查集数组,用于维护顶点之间的连通性。`find`函数是并查集的查找操作,用于查找顶点所在的集合。在主函数中,首先读入顶点数和边数,然后读入每条边并加入优先队列。接下来,按照克鲁斯卡尔算法的流程,不断从优先队列中取出最小的边,判断其所连接的两个顶点是否在同一个集合中,如果不在,则将它们合并,并加入最小生成树。最后输出最小生成树的权值和即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值