最小生成树定义
算法思路
Kruskal算法的思路比较直接,只需将连通网全部边按权值的大小由低到高排序,然后逐一将较小权值边加入到最小生成树的边集TE中即可。
算法构造过程
1. 选取当前权值最小的边(u,v),判断该边的加入是否会形成回路。若否,则将(u,v)加入到TE中。
2. 重复步骤(1),直到已加入TE的边的个数等于n-1为止。
辅助数组
构造辅助数组Assistarr[Marc],存放连通网的边并将边依据权值的大小从低到高排序;构造辅助数组setvex[Mvnum]记录顶点u与v的所在连通分量的位置,判断是否处于u与v是否同在一个连通图中。若否,将边(u,v)加入到TE中。该数组在初始化时,每一个顶点自成一个连通分量。
代码实现
存储结构为邻接表
//文件名为:ALGraph.h
#pragma once
#include<iostream>
using namespace std;
#define Mvnum 100 //最大顶点数
#define Marc 1000
/*
邻接表实现遍历
*/
typedef int OtherInfo; //边的信息,权值等
typedef char VexType; //顶点的信息
//边结点
typedef struct ArcNode {
int adjvex;
struct ArcNode* nextarc;
OtherInfo info;
}ArcNode;
//表头结点表的存储结构
typedef struct {
VexType data; //顶点vex数据域
ArcNode* fisrtarc;
}vNode, Adjlist[Mvnum];
//图的存储结构
typedef struct ALGraph {
Adjlist vertices; //定义表头结点表数组
int vexnum, arcnum; //顶点数以及边的数目
}ALGraph;
//创建一个邻接表(无向网)
void CreateUDNal(ALGraph& G);
//删除该邻接表
void DelUDNal(ALGraph& G);
//文件名为:ALGraph.cpp
#include"ALGraph.h"
//以邻接表为存储结构
void CreateUDNal(ALGraph& G)
{
cin >> G.vexnum >> G.arcnum;
if (G.vexnum > Mvnum || G.arcnum > (G.vexnum - 1) * G.vexnum / 2)
{
return;
}
//输入顶点数据域的数据并且将指针域置空
for (int i = 0;i < G.vexnum;i++)
{
cin >> G.vertices[i].data;
G.vertices[i].fisrtarc = NULL;
}
//连接各个顶点,输入边的权值以及边的指向顶点信息
for (int i = 0;i < G.arcnum;i++)
{
int v1, v2, w;
cin >> v1 >> v2 >> w;
v1 -= 1;
v2 -= 1;
if (v1 >= Mvnum || v2 >= Mvnum)
{
i--;
continue;
}
//连接v1到v2的边,并且完善该边的信息
ArcNode* p1 = new ArcNode;
p1->adjvex = v2;
p1->info = w;
//注意编号从0开始
p1->nextarc = G.vertices[v1].fisrtarc;
G.vertices[v1].fisrtarc = p1;
//连接v2到v1的边,并且完善该边的信息
ArcNode* p2 = new ArcNode;
p2->adjvex = v1;
p2->info = w;
p2->nextarc = G.vertices[v2].fisrtarc;
G.vertices[v2].fisrtarc = p2;
}
}
void DelAlg(ArcNode*& p)
{
if (p == NULL)
{
return;
}
DelAlg(p->nextarc);
delete p;
cout << "删除成功!" << endl;
p = NULL;
}
//删除该邻接表(有点难度)
void DelUDNal(ALGraph& G)
{
for (int i = 0;i < G.vexnum;i++)
{
DelAlg(G.vertices[i].fisrtarc);
}
}
//文件名为:Kruskal.h
#pragma once
#include"ALGraph.h"
//先定义edge结构体,存储G所有的边,并且排序
typedef struct edge {
//存储顶点u
VexType head;
//存储顶点v
VexType tail;
//存储边的权值
OtherInfo lowcost;
}edge;
//辅助函数初始化
void edgeInit(const ALGraph&G, edge*Assistarr);
//排序函数
void edgeSort(edge* Assistarr, const int& length);
//确定顶点位置
int LocateVex(const ALGraph& G, const VexType& v);
//用克鲁斯卡尔算法生成最小生成树
void MiniSpanTree_Kruaskal(const ALGraph& G);
//文件名为:Kruskal.cpp
#include"Kruskal.h"
//注意操作对象必须为连通网,否则生成个屁
//辅助函数初始化
void edgeInit(const ALGraph& G, edge* Assistarr)
{
int pos = 0;
//将G的所有边的放进辅助数组中
//时间复杂度:O(e)
for (int i = 0;i < G.vexnum;i++)
{
//边的数组都在p里面
ArcNode* p = G.vertices[i].fisrtarc;
while (p)
{
//铛邻接表存储有向网时,需要分清楚弧头与弧尾
//说明有边可入
Assistarr[pos].head = G.vertices[p->adjvex].data;
Assistarr[pos].tail = G.vertices[i].data;
Assistarr[pos].lowcost = p->info;
pos++;
p = p->nextarc;
}
}
}
//排序函数
void edgeSort(edge* Assistarr,const int& length)
{
int flag = 0;
for (int i = 0;i < length - 1 ;i++)
{
//将较大的元素放在后面
for (int j = 0;j < length - 1 - i;j++)
{
if (Assistarr[j].lowcost > Assistarr[j + 1].lowcost)
{
//将两个元素交换
edge t = Assistarr[j];
Assistarr[j] = Assistarr[j + 1];
Assistarr[j + 1] = t;
flag = 1;
}
}
if (flag == 0)
break;
}
}
//初始化setvex
void setvexInit(int* setvex, const int& length)
{
for (int i = 0;i < length;i++)
{
setvex[i] = i;
}
}
int LocateVex(const ALGraph& G, const VexType& v)
{
for (int i = 0;i < G.vexnum;i++)
{
if (G.vertices[i].data == v)
return i;
}
}
//用克鲁斯卡尔算法生成最小生成树
void MiniSpanTree_Kruaskal(const ALGraph& G)
{
//定义辅助数组,存储图的边,并且排序
edge Assistarr[Marc];
//初始化辅助数组,数组大小为arcnum
edgeInit(G, Assistarr);
//将数组排序
edgeSort(Assistarr, 2*G.arcnum);
int setvex[Mvnum] = { 0 };
setvexInit(setvex, G.vexnum);
//生成最小生成树
int pos = 0;
for (int i = 1;i < G.vexnum;i++)
{
//每一次循环都加入一条新的路径
VexType head = Assistarr[pos].head;
VexType tail = Assistarr[pos].tail;
//先判断这两个顶点是否相连
//找这两个顶点的下标
int h = LocateVex(G, head);
int t = LocateVex(G, tail);
//判断两个顶点是否同在一个连通分量里面
if (setvex[h] != setvex[t])
{
cout << G.vertices[h].data << "->" << G.vertices[t].data << endl;
}
else
{
pos++;
i--;
continue;
}
int s = setvex[t];
for (int k = 0;k < G.vexnum;k++)
{
if (setvex[k] == s)
{
setvex[k] = setvex[h];
}
}
pos++;
}
}
测试
//文件名:test.cpp
#include"Kruskal.h"
void test01()
{
ALGraph G;
CreateUDNal(G);
MiniSpanTree_Kruaskal(G);
DelUDNal(G);
}
int main()
{
test01();
return 0;
}
测试结构

算法分析
对于包含e条边的网,上述的排序算法若改为快速排序,则时间复杂度为O(ee)。在for循环中最耗时的操作是合并两个不同的连通分量,只要采取合适的数据结构,可以证明其执行时间为O(log e),因此整个for循环的执行时间是O(eloge),由此,Kruskal算法的时间复杂度为O(eloge),与网中的边有关,与Prim算法相比,Kruskal算法更适合于求稀疏网的最小生成树.