Bootstrap

数据结构实验——用邻接表存储,并按Kruskal算法求最小生成树

用邻接表存储,并按Kruskal算法求最小生成树 实验

3.1 实验内容

根据书P262习题3给定的无向带权图,用邻接表作为存储结构,用kruskal算法构造其最小生成树。

克鲁斯卡尔算法的基本思想是:设一个有n个顶点的连通网络G={V,E},先构造一个包括全部n个顶点和0条边的森林F={T0,T1,…,Tn-1},以后每一步向F中加入一条边(v, u),它应是所依附的两个顶点v和u分别在森林F的两棵不同的树上的所有边中具有最小权值的边。由于这条边的加入,使F中的某两棵树合并为一棵,树的棵数减一。如此,经过n-1步,最终得到一棵有n-1条边且各边权值总和达到最小的生成树--最小生成树。

3.2文件结构、开发环境等说明

开发环境版本

VisualStudio 2022

工程文件名

ex2_2_prim.sln

头文件个数

7个

源程序文件个数

1个

文件名

文件类型

功能简介

备注

AdjListDirNetwork.h

头文件

邻接表类模板头文件,包括数据成员及成员函数的声明与定义

AdjListDirNetworkArc.h

头文件

邻接表边结点类模板头文件,包括数据成员及成员函数的声明与定义

AdjListDirNetworkVec.h

头文件

邻接表顶点结点类模板头文件,包括数据成员及成员函数的声明与定义

Kruskal.h

头文件

Kruskal算法实现最小生成树的构造

Assistance.h

头文件

辅助软件包

MineHeap.h

头文件

最小堆类模板头文件,包括数据成员及成员函数的声明与定义

UFSets.h

头文件

并查集类模板头文件,包括数据成员及成员函数的声明与定义

text.cpp

源文件

测试文件

3.3实现技术

1、邻接表作为存储结构

通过三个头文件“AdjListDirNetwork.h”、“AdjListDirNetworkArc.h”、“AdjListDirNetworkVec.h”,用邻接表类、邻接表边结点类、邻接表顶点结点类将图用邻接表的存储结构表示(这里不展示相关代码)。

2、最小堆存放连通网络中的边

在克鲁斯卡尔算法中,利用最小堆来存放连通网络中的边,堆中每个元素代表连通网络中的一条边(这里不做堆类的代码展示)

3、并查集存放连通分量

在利用并查集存放所有连通分量,同一个连通分量的顶点组成并查集的一个子集(等价类)。(这里不做并查集类的代码展示)

4、Kruskal算法

为了在堆中存放边的信息,并在堆调整时进行边上权值比较,下面先给出Kruskal算法中边类声明和实现。在这个边类中定义了一条边依附的两个顶点vertexl、vertex2以及边的权weight。另外,除了构造函数外,重载了赋值运算、<=和>两个关系运算。

template<class ElemType,class WeightType>
class KruskalEdge
{
public:
	ElemType vertex1, vertex2;
	WeightType weight;
	KruskalEdge(ElemType v1, ElemType v2, WeightType w);
	KruskalEdge() { vertex1 = 0; vertex2 = 0; weight = 0; };
	KruskalEdge<ElemType, WeightType>& operator=(const KruskalEdge<ElemType, WeightType>& Ed);//赋值语句重载
	bool operator<=(const KruskalEdge<ElemType, WeightType>& Ed);
	bool operator>=(const KruskalEdge<ElemType, WeightType>& Ed);
	bool operator>(const KruskalEdge<ElemType, WeightType>& Ed);
	bool operator<(const KruskalEdge<ElemType, WeightType>& Ed);
};

克鲁斯卡尔算法步骤如下。

1)初始化,在并查集中,连通网络的每一个顶点独立成一个等价类,连通网络的所有的边建立最小堆,最小生成树T中没有任何边,T中边的条数计数器i为0。

2)如果T中边的条数计数器i等于顶点数减1,则算法结束;否则继续步骤3)。

3)选取堆顶元素代表的边(v,u),同时调整堆。

4)利用并查集的运算检查依附于边(vu)的两个顶点v和u是否在同一个连通分量(即并查集的同一个子集合)上,如果是则转步骤2);否则继续步骤5)。

5)将边(v,u)加入到最小生成树T中,同时将这两个顶点所在的连通分量合并成一个连通分量(即并查集中的相应两个子集合并成一个子集),继续步骤2)。

template <class ElemType, class WeightType>
void MiniSpanTreeKruskal(const AdjListDirNetwork<ElemType, WeightType>& g)
{
	int count, VexNum = g.GetVexNum();
	KruskalEdge<ElemType, WeightType> KEdge;
	MineHeap<KruskalEdge<ElemType, WeightType> > ha(g.GetEdgeNum());
	ElemType* kVex, v1, v2;
	kVex = new ElemType[VexNum];	// 定义顶点数组,存储顶点信息 
	for (int i = 0; i < VexNum; i++)
		g.GetElem(i, kVex[i]);
	UFSets<ElemType> f(kVex, VexNum);// 根据顶点数组构造并查集 
	for (int v = 0; v < VexNum; v++)
		for (int u = g.FirstAdjVex(v); u >= 0; u = g.NextAdjVex(v, u))
			if (v < u) {	// 将v < u的边插入到最小堆 
				g.GetElem(v, v1);
				g.GetElem(u, v2);
				KEdge.vertex1 = v1;
				KEdge.vertex2 = v2;
				KEdge.weight = g.GetWeight(v, u);
				ha.Insert(KEdge);
			}
	count = 0;					    // 表示已经挑选的边数
	while (count < VexNum - 1) {
		ha.DeleteTop(KEdge);        // 从堆顶取一条边
		v1 = KEdge.vertex1;
		v2 = KEdge.vertex2;
		if (f.Differ(v1, v2)) {	// 边所依附的两顶点不在同一棵树上
			cout << "边:( " << v1 << ", " << v2 << " ) 权:" << KEdge.weight << endl; // 输出边及权值
			f.Union(v1, v2);		// 将两个顶点所在的树合并成一棵树
			count++;
		}
	}
}

5、测试代码

调用MiniSpanTreeKruskal(net)函数,生成最小生成树(完整代码在资源区)

cout << "Kruskal算法产生最小生成树的边:" << endl;
MiniSpanTreeKruskal(net);
cout << endl;

3.4测试结果

1、测试实例

2、邻接表作为存储结构

3、Kruskal算法

完整代码可看资源区

创作不易~麻烦点个赞~~谢谢大家~~~ 

 

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;