贪心算法 - 最小生成树 Prim算法

一个无向带权图G=(V,E),其中n个顶点Vertex,以及连接各个顶点之间的边Edge,可能有些顶点之间没有边,每条边上的权值都是非负值。

生成树:

G的一个子图,包含了所有的Vertex,和部分的Edge。

最小生成树:

所有的生成树中,各条Edge上的权值总和最小的一个。

例子:设计通信网络时,各个城市之间铺设线路,最经济的方案。

最小生成树性质:

G=(V,E),

S是V的真子集,

如果u在S中,v在V-S中,且(u,v)是图的一条边,称之为特殊边,且(u,v)是所有特殊边中最短的,

那么,(u,v)这条边一定在最小生成树中。


Prim算法:

任意指定一个顶点作为起始点,放在S中。

每一步将最短的特殊边放入S中,需要n-1步,即可把所有的其他的点放入S中。算法结束。


对于这个图,Prim算法的过程为:



代码实现如下:

/**
 * 最小生成树 minimum spanning tree
 * @author xuefeng
 *
 */
public class MST {

	public static final int NOT_REACHED = -1;

	/**
	 * @param E n*n
	 */
	public static void prim(int[][] E) {
		int n = E.length;

		boolean[] S = new boolean[n];
		int[] dist = new int[n];
		int[] prev = new int[n];

		S[0] = true; // 选取顶点1作为起始点
		for (int i = 1; i < n; i++) {
			dist[i] = E[0][i];
			prev[i] = 0;
			S[i] = false;
		}

		int min = -1, minV = -1;
		for (int i = 1; i < n; i++) {
			min = -1;
			minV = -1;
			// 选择离S中顶点最近的点
			for (int j = 1; j < n; j++) {
				if (!S[j] && dist[j] != -1 && (min == -1 || dist[j] < min)) {
					min = dist[j];
					minV = j;
				}
			}
			if (minV == -1)
				continue;
			
			S[minV] = true;

			// S中多了个点,需要改变S离外面的点的最短距离
			for (int j = 1; j < n; j++) {
				if (!S[j] && isReachable(E, minV, j)
						&& (dist[j] == -1 || E[minV][j] < dist[j])) {
					dist[j] = E[minV][j];
					prev[j] = minV;
				}
			}

			// 输出测试结果
			for (int j = 0; j < n; j++) {
				if (S[j]) {
					System.out.print((j + 1) + " ");
				}
			}
			System.out.println();
		}

	}

	private static boolean isReachable(int[][] E, int v1, int v2) {
		return E[v1][v2] != NOT_REACHED;
	}

	public static void main(String[] args) {
		int[][] E = { { -1, 6, 1, 5, -1, -1 }, { 6, -1, 5, -1, 3, -1 },
				{ 1, 5, -1, 5, 6, 4 }, { 5, -1, 5, -1, -1, 2 },
				{ -1, 3, 6, -1, -1, 6 }, { -1, -1, 4, 2, 6, -1 } };

		prim(E);
	}
}

输出为:

1 3 
1 3 6 
1 3 4 6 
1 2 3 4 6 
1 2 3 4 5 6 

可由prev数组构造出最小生成树。

算法复杂度为O(n^2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值