简介
在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得 w(T) 最小,则此 T 为 G 的最小生成树。最小生成树其实是最小权重生成树的简称。
许多应用问题都是一个求无向连通图的最小生成树问题。例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同;另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。
构建思路
在每一次循环迭代前,A是某个最小生成树的一个子集.在算法的每一步确定一条边(u,v),使得其加入集合A后,集合A任然保持为一棵最小生成树,而每次加入的边(u,v)称为安全边(即是加入后不破坏循环不变式的边)
GENERIC-MST(G, w)
A = 空集
while A does not form a spanning tree
find an edge (u,v) that is safe for A
add (u, v) into A
return A
由上可见,寻找安全边是算法的关键,下面对最小生成树的部分性质进行证明。从而解决该问题.
最小生成树性质
算法实现
Kruskal(克鲁斯卡尔)算法
概述
Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。
伪代码
下面代码是以算法导论中的kruskal的伪代码改写而来的,总体思路为:
1.按照权值由低到高的顺序寻找安全边,并将其加入最小生成树中.
2.在安全边的选择上还需注意当发现该边的端点都在已构建完成的最小生成树中时表明如果加入该边将形成环,因此这种情况认为该边不安全.(简而言之安全边就是不破环循环不变式的边)
4.该算法的成立依赖于前面证明的最小生成树的性质.
MST-KRUSKAL.G(G, w)
tree = 空
sort the edges of G.E into nondecreasing order by weight w
foreach edge(u,v) in G.E, taken in nondecreasing order by weight
do if u and v are not both in the same tree
add edge (u, v) into tree
return tree
代码实现
/**
* @brief code for spanning tree
* @author xiyan
* @date 2014/07/01
*
*/
#include <vector>
#include <iostream>
#include <algorithm>
namespace sptree{
using namespace std;
class Edge{
public:
int lft;
int rht;
int weight;
};
struct edgeComapre {
bool operator() (Edge lft, Edge rht) { return (lft.weight < rht.weight);}
} edgeCompareObj;
class Kruskal{
public:
Kruskal(void):tot(0) { tmpClean(); }
int init(const int &posSize);
int insert(const class Edge nedge);
int build(void);
virtual ~Kruskal(void){ tmpClean(); }
private:
void tmpClean(void);
int findSet(const int &pos);
void makeUnion(const int &lft, const int &rht);
vector<class Edge> edges;
int *fatherA;
int posSize;
int tot;
};
int Kruskal::insert(const class Edge nedge) {
if(nedge.lft >= posSize || nedge.rht >= posSize)
return - 1;
edges.push_back(nedge);
return 0;
}
void Kruskal::tmpClean(){
edges.clear();
if(fatherA){
delete fatherA;
fatherA = NULL;
}
}
int Kruskal::findSet(const int &pos){
int rootPos;
int currPos = pos;
while(fatherA[currPos] != currPos){
currPos = fatherA[currPos];
}
rootPos = currPos;
currPos = pos;
while(currPos != rootPos){
int fatherPos = fatherA[currPos];
fatherA[currPos] = rootPos;
currPos = fatherPos;
}
return rootPos;
}
int Kruskal::init(const int &iposSize){
if(iposSize <= 0)
return -1;
posSize = iposSize;
if( NULL == ( fatherA = new int[posSize])){
return -1;
}
for(int currPos = 0; currPos < posSize; currPos++){
fatherA[currPos] = currPos;
}
return 0;
}
int Kruskal::build(void){
int ret = 0;
sort(edges.begin(), edges.end(), edgeCompareObj);
for(vector<class Edge>::iterator iter = edges.begin(); iter != edges.end(); iter++){
int lftRoot = findSet(iter->lft);
int rhtRoot = findSet(iter->rht);
if(lftRoot == rhtRoot){
continue;
}
makeUnion(lftRoot, rhtRoot);
ret += iter->weight;
}
tmpClean();
return ret;
}
void Kruskal::makeUnion(const int &lft, const int &rht){
fatherA[lft] = rht;
}
}
using namespace sptree;
int main(){
class Kruskal *krup = new class Kruskal;
class Edge nedge;
if(!krup){
cout << "new kruskal fail" << endl;
return -1;
}
if(krup->init(20) < 0){
cout << "set max pos fail" << endl;
return -1;
}
cout << "please input info for every edge" << endl;
while(cin >> nedge.lft >> nedge.rht >> nedge.weight){
krup->insert(nedge);
cout << "please input info for every edge" << endl;
}
cout << krup->build() << endl;
return 0;
}
普里姆(Prim算法)算法
概述
图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。
伪代码
/*该算法依赖于定理23.2*/
MST-PRIM(G,w, r) /*初始化非起始节点*/
for each u 属于 V - {r}
u.key = 无穷大
u.father = NIL
push u into Q
r.key = 0; /*初始化起始节点*/
r.father = r;
push u into Q
while Q != 空
u = pop element from Q wich with min key
for each edge (u, v)
if v not in Q and w(u , v) < v.key
v.key = w(u , v)
v.father = u
Boruvka算法
参考文章
<算法导论>
http://baike.baidu.com/view/288214.htm?fr=aladdin
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html