图-最小代价生成树

  一棵带权无向图的生成树代价是该树中所有边权之和,最小代价生成树就是一棵代价最小的生成树。

可以通过三种算法获得最小代价生成树:kruskal,prim,soilion算法,都是贪心算法

1.kruskal算法

  算法思想:每次向最小代价生成树中T加入一条边的方法构造最终的最小生成树,按照边代价非递增顺序选取边。kruskal算法要求每次加入的没有环,可以用上次二叉树森林中集合表示,首先有n个点的集合(森林),如果选边<i,j>,如果i,j不在同一个集合中,可以合并,如果在同一个,则存在环。且每次选择最小的边,可以用最小堆来实现。

kurskal
//克鲁斯卡尔算法,求最小生成树,找最小的边,且不存在回路.时间复杂度(eloge)
void Graph::Kruskal()
{

set_edge_set();
Forest ver_set(vertex);
//vertexd多个顶点的森林
MinHeap<Edge> heap(edge_set.size()+1);
for(vector<Edge *>::const_iterator iter=edge_set.begin();iter!=edge_set.end();++iter)
{
//每个定点插入到堆中(小根堆)
heap.Insert(*(*iter));
}

vector
<Edge> T; //最小生成树的边数

cout
<<"Kruskal Algorithm : "<<endl;

while (1)
{
//找出最小边
Edge min_edge=heap.Delete();
int from=ver_set.Find(min_edge.from); //最小边from所在的集合
int to=ver_set.Find(min_edge.to); //最小边to所在的集合

if (from==to) //from 和 to 在同一个集合中,存在环路
continue;
else
{
//不存在环路,合并from 和to两个集合
ver_set.Union(from,to);

//加入到边集中
T.push_back(min_edge);
}

//已找到所有的生成树的边集
if (T.size()==vertex-1)
{
break;
}

if (heap.isEmpty())
{
//边不够,不能构成最小生成树
break;
}
}
if (T.size()==vertex-1)
{
//输出最小生成树
cout<<"the spanning tree is :";
for (vector<Edge>::iterator iter1=T.begin();iter1!=T.end();++iter1)
{
iter1
->print();
}
cout
<<endl;

}
else
{
//没有最小生成树
cout<<"\tNo spanning tree"<<endl;
}

cout
<<"End"<<endl;

}

 算法分析:kruskal算法可以再O(e)时间内构造最小堆,并可以在O(loge)找出最小边,所以kruskal算法总时间复杂度O(eloge)

2.prim算法

  prim算法每次选择一棵边构成树,而kruskal算法每次选择构成一个森林,prim算法从包含一个顶点树T开始(图中任意顶点)。把一棵代价最小的(u,v)加入T中,是T仍是一棵树,知道包含n-1个边为止,为了选(u,v),只能有一个顶点在T中。

//T 是生成树边集
//Tv生成树顶点集

T={}
TV={0} //start with vertex 0 and no edges
while(T contains fewer than n-1 edges)
{
  let(u,v) be a least cost such that u belong to TV and v not belong
  if (there is no such edge)
    break;
  add v to TV;
  add(u,v) to T;
}
prim算法
 1 // 普里姆最小生成树 
2 void Graph::Prim()
3 {
4 vector<Edge> T;
5 set<int> U; //不在生成树集合中点集
6 set<int> V; //生成树中点集
7 int distance[max_vertexs][2];
8
9 static int MAX=9999;
10
11 //初始化
12 distance[0][0]=-1;
13 distance[0][1]=0;
14 V.insert(0);
15 for (int i=1;i<vertex;i++)
16 {
17 U.insert(i);
18 distance[i][0]=MAX; //表示结点i到V 的最短距离
19 distance[i][1]=0; //i 到V最短几点v
20 }
21
22 int last=0; //上一次加入V的结点
23
24 //找出U中到V中集合最优路径,(u,v) 并加入T中
25 while (true)
26 {
27 int min_cost=MAX;
28 int u=0;
29
30 //从U中到V中最小距离 (u,v)
31 for (set<int>::iterator iter=U.begin();iter!=U.end();++iter)
32 {
33 i=*iter;
34 if (distance[i][0]>0)
35 {
36 if (graph[i][last]>0 && graph[i][last]<distance[i][0])
37 {
38 distance[i][0]=graph[i][last];
39 distance[i][1]=last;
40 }
41 if (distance[i][0]<min_cost)
42 {
43 min_cost=distance[i][0];
44 u=i;
45 }
46 }
47
48 }
49
50 if (min_cost==MAX)
51 {
52 break;
53 }
54 else
55 {
56 //新的u加入到V中,并从U中去点
57 Edge temp(u,distance[u][1],distance[u][0]);
58 distance[u][0]=-1;
59 T.push_back(temp);
60 V.insert(u);
61 U.erase(u);
62 last=u;
63 }
64 }
65
66 cout<<"Prim Algorithm :"<<endl;
67
68 if (T.size()==vertex-1)
69 {
70 //输出最小生成树
71 cout<<"the spanning tree is : ";
72 for (vector<Edge>::iterator iter1=T.begin();iter1!=T.end();++iter1)
73 iter1->print();
74 cout<<endl;
75 }
76 else
77 {
78 //没有最小生成树
79 cout<<"No spanning tree"<<endl;
80 }
81 cout<<"Over!"<<endl;
82
83 }

   实现:初始时V={0},构造一个矩阵distance[0,n-1],distance[i]表示 i 到T中顶点的最短距离,如果i已在T中值为0。在选择点u加入T中,首先,在distace中找出未加的顶点即distace[i]>0,且最小的点u加入,u加入到T后 要跟新distace[u]=-1,记录u。依次在在distace中找出最小的,不过遍历是如果grap[u][i]<distace[i],则表示u加入后,对没加入T的顶点到T中有影响,先更新distace[i]。再在distace中找出最小的加入,直到没有可加入的点。

分析:prim算法O(n^2)时间复杂度内实现

 

 

posted on 2011-09-09 22:11  youngkang  阅读(4814)  评论(0)    收藏  举报