文章目录
一、最小生成树概述
1.1 背景概述
问题描述:
假设要在 n 个城市之间建立通讯联络网,则连通 n 个城市只需要修建 n-1 条线路,每条城市之间的线路修建成本是不完全相同的,如何在最节省经费的前提下建立这个通讯网?
问题分析:
n 个城市间,最多可设置 n(n-1)/2 条线路;n 个城市间建立通信网,只需 n-1 条线路。问题转化为:在可能的线路中选择 n-1 条,能把所有城市(顶点)均连起来,且总耗费(各边权值之和)最小(即建立该通信网所需花费的总代价最小)。
1.2 最小生成树的定义
在一个带权连通无向图 G 中可能找到许多生成树,生成树是一棵包含 G 的所有顶点的树,树上所有权值总和表示代价。由于生成树只有有限个,其中必然有权值之和最小的。那么在 G 的所有的生成树中,代价最小的生成树称为图 G 的最小生成树(minimum-cost spanning tree,简称 MST)。
上面的问题等价于构造城市网的一棵最小生成树,在所有带权的边中选取 n-1 条边(不构成回路),使权值之和为最小。
1.3 最小生成树构造算法
构造最小生成树可以有多种算法。其中多数算法利用了最小生成树的简称为 MST 的性质:
假设 N =(V, {E}) 是一个连通网,U 是顶点集 V 的一个非空子集。若 u、v 两个顶点之间的边(u, v)是一条具有最小权值(代价)的边,其中 u ∈ U,v ∈ V-U,则必存在一颗包含边(u, v)的最小生成树。
利用 MST 性质构造最小生成树的经典算法有两个:
- 普里姆(Prim)算法
- 克鲁斯卡尔(Kruskal)算法
其中,普里姆算法时间复杂度为 O(n²)(n 为顶点个数),适合求解边稠密的网的最小生成树;克鲁斯卡尔算法的时间复杂度为 O(eloge),适合求解边稀疏的网的最小生成树。
二、普里姆算法
2.1 普利姆算法介绍
普利姆算法内容如下:
假设 N = (V,{ E })是连通网,TE 是 N 上最小生成树中边的集合。算法从 U = { u₁ }(u₁ ∈ V),TE = { } 开始,重复执行下述操作:在所有 u ∈ U,v ∈ V-U 的边(u,v)∈ E 中找一条代价最小的边(u₁,v₁)并入集合 TE,同时 v₁ 并入 U,直至 U = V 为止。此时 TE 中必有 n-1 条边,则 T =(V,{ TE })为 N 的最小生成树。
从算法的定义上来看,确实不太容易理解,下面将图解演示使用普里姆算法求下面的图形结构的最小生成树的过程。
-
首先,约定 V 为所有顶点的集合;约定 TE 为最小生成树中边的集合,初始为空;约定 U 为最小生成树中边的顶点集合,初始为空。
-
选择从 A 点开始普里姆算法,则 U = { A }。
-
找出所有与集合 U 中的顶点相连的权重最小的边作为最小生成树的边。此时集合 U 中只有一个 A顶点,在集合 V-U 中与 A 顶点相连的边有 <A,B>、<A,C>、<A,D>。其中权重最小的边为 <A,C>,因此我们选择其为最小生成树的一条边,然后将此边加入 TE 集合,将 C 顶点加入 U 集合。此时 TE = { <A,C> },U = { A,C }。
-
继续找出所有与集合 U 中顶点相连的权重最小的边作为生成树的边。此时集合 U = { A、C },在顶点集合 V-U 中分别与顶点 A、C 相连的边有 <A、B>、<A、D>、<C、B>、<C、D>、<C、E>。其中权重最小的边为 <C、D>,因此我们选取其为最小生成树的边,加入 TE 集合,将顶点 D 加入 U 集合。此时 TE = { <A,C> ,<C,D> },U = { A,C,D }。
-
继续找出所有与集合 U 中顶点相连的权重最小的边作为生成树的边。此时集合 U = { A、C、D },在顶点集合 V-U 中分别于它们相连的边有 <A、B>、<C、B>、<C、E>、<D、E>。其中权重最小的边为 <C、E>,因此我们选取其为最小生成树的边,加入 TE 集合中,将顶点 E 加入 U 集合中。此时 TE = { <A,C> ,<C,D> ,<C、E> },U = { A,C,D,E }。
-
继续找出所有与集合 U 中顶点相连的权重最小的边作为生成树的边。此时集合 U = { A,C,D,E },在顶点集合 V-U 中分别于集合 U 中顶点相连的边有 <A、B>、<C、B>、<E、B>。其中权重最小的边为 <C、B>,因此我们选取其为最小生成树的边,加入 TE 集合中,将顶点 B 加入到 U 集合中。此时 TE = { <A,C> ,<C,D> ,<C、E>,<C,B> },U = { A,C,D,E,B }。
至此,集合 U 等于了 V,也就是构造出了最小生成树,普里姆算法结束。
2.2 普利姆算法代码实现
问题描述:
上图代表 7 个城市之间的交通网,边上的权值代表公路的造价。现在要用公路把这 7 个城市连接起来,则至少需要修建 6 条路。如何设计使得这 6 条公路的总造价最少呢?
代码实现:
/**
* @Description 普里姆算法解决修路问题
*/
public class PrimAlgorithm {
public static void main(String[] args) {
char[] vertex = {
'A','B','C','D','E','F','G'}; // 顶点
int[][] weight = {
// 邻接矩阵,约定 10000 代表不连通
/*A*//*B*//*C*//*D*//*E*//*F*//*G*/
/*A*/{
10000,5,7,10000,10000,10000,2},
/*B*/{
5,10000,10000,9,10000,10000,3},
/*C*/{
7,10000,10000,10000,8,10000,10000},
/*D*/{
10000,9,10000,10000,10000,4,10000},
/*E*/{
10000,10000,8,10000,10000,5,4},
/*F*/{
10000,10000,10000,4,5,10000,6},
/*G*/{
2,3,10000,10000,