最小生成树——Kruskal算法

最小生成树定义

见链接:http://t.csdn.cn/yI3mq

算法思路

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
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;
}

测试结构

最小生成树——Kruskal算法_第1张图片

算法分析

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

你可能感兴趣的:(数据结构与算法,C++,算法,深度优先,数据结构,图论)