(1小时数据结构)数据结构c++描述(二十九)--- 图(最小生成树)

最小代价生成树 :

        一个无向连通图的生成树是一个极小连通子图,它包括图中全部顶点,并且有尽可能少的边。遍历一个连通图得到图的一棵生成树。图的生成树不是唯一的,采用不同的遍历方法,从不同的顶点出发可能得到不同的生成树。 
        一棵生成树的代价是各条边上的代价之和。一个网络的各生成树中,具有最小代价的生成树称为该网络的最小代价生成树。
 

约定来简化定义:

        必须是一个无向的连通图,生成的最小生成树的结果是不一样的。

著名的求最小代价生成树有2种方法:Prim算法和Kruskal算法。 

普里姆算法:

  • 初始状态下,这棵生成树只有一个顶点,没有边,v0是任意选定的顶点。 
  • 从初始状态出发,按照某种准则,每一步从图中选择一条边,共选取n-1条边,构成一棵生成树。 
  • 选边准则是:寻找一条代价最小的边(u,v),边(u,v)是所有一个顶点u在构造中的生成树上,而另一个顶点v不在该树上的边(u,v)中代价是最小的。

普里姆算法图解:

(1小时数据结构)数据结构c++描述(二十九)--- 图(最小生成树)_第1张图片

普里姆算法代码:

//普里姆算法求无向图最小代价生成树,外部接口
template
 void Graph::Prim(int v0)
{
	 if (v0 < 0 || v0 > n - 1) {
		 cout << "input error!" << endl;
		 return;
	 }
	 int *nearest = new int[n];      //nearest[v]=u,表示离v最近的是u,其中u在最小生成树中,v是待加入的顶点
	 T *lowcost = new T[n];          //lowcost[v]=w(u,v),表示离v最近的点u之间的权值

	 for (int i = 0; i < n; ++i) {
		 nearest[i] = -1;
		 lowcost[i] = INT_MAX;
	 }

	 Prim(v0, nearest, lowcost);
	 cout << "(nearest[i],i,lowcost[i]) = ";
	 for (int i = 0; i < n; ++i) {
		 cout << "(" << nearest[i] << "," << i << "," << lowcost[i] << ")" << " ";
	 }
	 cout << endl;
	 delete[]nearest;
	 delete[]lowcost;
}


//普里姆算法求无向图最小代价生成树,私有,内部调用
template
void Graph::Prim(int v0, int * nearest, T * lowcost)
{
	bool *mark = new bool[n];
	for (int i = 0; i < n; ++i) {
		mark[i] = false;
	}
	ENode *p = NULL;
	nearest[v0] = v0;
	lowcost[v0] = 0;
	mark[v0] = true;

	int k = v0;         //最近加入生成树中的顶点

	for (int i = 1; i < n; ++i) {
		for (p = enodes[k]; p; p = p->next) {   //更新nearest和lowcost
			int j = p->adjVex;
			if (!mark[j] && lowcost[j] > p->weight) {
				nearest[j] = k;
				lowcost[j] = p->weight;
			}
		}
		T min = INT_MAX;
		for (int j = 0; j < n; ++j) {       //找到最小代价的边
			if (!mark[j] && min > lowcost[j]) {
				min = lowcost[j];
				k = j;
			}
		}
		mark[k] = true;
	}
}

克鲁斯卡尔算法 :

  • 初始状态,将所有顶点看成由n棵树组成的森林T,没有边。从初始状态开始,采用每一步选择一条边,共选n-1条边,构成一棵最小代价生成树。 
  • 选边准则是:在所有边的集合E中选择一条代价最小的边(u,v),并将其从E中删除;若在T中加入边(u,v)后不形成回路,则将其加进T中(这就要求u和v分属于生成森林的两棵不同的树上,由于边(u,v)的加入,这两棵树连成一棵树),否则继续选择下一条边。直到森林T变成一棵树,也就是生成了最小代价树。 

克鲁斯卡尔图解:

 (1小时数据结构)数据结构c++描述(二十九)--- 图(最小生成树)_第2张图片

克鲁斯卡尔算法代码:

//克鲁斯卡尔算法求无向图最小代价生成树,外部接口
template
void Graph::Kruskal()
{
	priority_queue, vector > > pq;   //最小优先队列

	for (int i = 0; i < n; ++i) {
		for (ENode *w = enodes[i]; w; w = w->next) {
			pq.push(*w);
		}
	}
	Kruskal(pq);
}

//克鲁斯卡尔算法求无向图最小代价生成树,私有,内部调用
template
void Graph::Kruskal(priority_queue>& pq)
{
	ENode kruskalResult[n - 1];
	ENode x;


	//n个结点只要加入n-1条边就可以
	int k = 0;      //已加入最小生成树中的边数

					//每次从pq中取出具有最小代价的边,并且该边的两端顶点不会都已经在树中了
	while (k < n - 1 && !pq.empty()) {
		x = pq.top();
		pq.pop();
		if (!uf->Connected(x.vertex, x.adjVex)) {  //如果找到的最小边两端点还没在最小树中相连
			kruskalResult[k] = x;
			uf->Union(x.vertex, x.adjVex);        //将两端点在树中相连
			k++;
		}
	}

	cout << "(u,v,weight) = ";
	for (int j = 0; j < n - 1; ++j) {
		cout << "(" << kruskalResult[j].vertex << "," << kruskalResult[j].adjVex << "," << kruskalResult[j].weight << ") ";
	}
	cout << endl;
}

       有关基础的定义在我博客图的章有全部源码,可以关注我的博客,在数据结构与算法的章节,有相关的源码,与对应的博客,小伙伴们,一起加油吧。

 

你可能感兴趣的:(数据结构与算法,数据结构,c++,算法,Kruskal,prim)