克鲁斯卡尔算法的构造解释及代码编写

一,大致的遍历过程图解及过程讲解

总连通网如右下角所示。

从所有权值中找到最小值,若不连通,则添加该边无回路则正确

1,如下图(a),先找到最小边AC,加入;

2,图(b),找到下一个最小边DF,加入;

3,图(c),图(d),按照大小排序,一次找到BE,CF加入;

4,图(e),因为有三个边的长度都为5,但是由图可知,当AD或者CD加入时,都会产生回路,而BC正好合适,所以BC加入

克鲁斯卡尔算法的构造解释及代码编写

二,实现方法

第一步:构造邻接矩阵,只不过这保存方法与前面不同.

              只需要构造一个能够保存每条边起点Head终点Tail和权值lowcost的结构体数组。

第二步:将上面构造的结构体数组进行从小到大排序。

              本文代码运用的是归并排序https://blog.****.net/qq_46423166/article/details/105493704

              也可以用冒泡排序,插入排序,选择排序,快速排序都等可以,可以看以往博客。

              但是这里注意交换值的时候一次要交换结构体里面的所有值。我这里一次就要交换Head,Tailm,lowcast三个的值

             eg:      S1[k].lowcost = S[i].lowcost;
                           S1[k].Head = S[i].Head;
                           S1[k].Tail = S[i].Tail;(将S[i]的值赋给S1[k])

第三步:进行克鲁斯卡尔算法

               即再将上面排序好的结构体数组进行遍历,符合要求即输出。

              此时需要判断的就是加入某一边,是否会产生环,所以加入一个新的数组Vexset[ ],每个顶点一个数组位置;

              刚开始数组都赋值为本身,即自身和自身相连,假设当n点可以连接m时,将Vexset[n]=m(最后产生的结果就是,一条连通的边上,每个顶点的Vexset值都一样),此时若新加入的顶点的Vexset与将要加入的连通边相同,则判断错误,不能加入。

eg:如下图运行截图表现出的数组Vexset的值

0 1 2 3 4 5(最开始赋值,每个顶点连接自己)

0 1 0 3 4 5(AC连接,所以将C的值变成0)

0 1 0 3 4 3(DF连接,所以将F的值变成3)

0 1 0 3 1 3(BE连接,所以将E的值变为1)

0 1 0 0 1 0(CF连接,所以此时ACFD成为连通边,所以都变成一样的值(用的循环判断赋值),,所以ACDF都变为0)

下面多出来的一次0 1 0 0 1 0,是因为出现了边的连通情况,若这一步时AD因为AD的Vexset都为0,所以不能连通。

最后加入BE边,此时六个点全部连通,所以值都变成1(可能此处赋值是按照BE连通边的值进行赋值,不是用ACDF赋值)

运行截图:

克鲁斯卡尔算法的构造解释及代码编写

代码:

#include<iostream>
#include<stdlib.h>
using namespace std;

#define pointMax 30
#define cloEdge 30

struct  Node
{
    char Head;                       //边的始点
    char Tail;                       //边的终点
    int lowcost;                       //权值
};

int Vexset[cloEdge];

struct AMgroup
{
    char VTchart[pointMax];                  //顶点表
    int point, vert;                         //点,边
};

int AMlocate(AMgroup &Map, char x)                        //找到某一顶点的位置
{
    for (int i = 0; i < Map.point; i++)
    {
        if (Map.VTchart[i] == x)
        {
            return i;
            break;
        }
    }
    cout << "error;";
    return -1;
}

//邻接矩阵创造,以及边的赋值
void CreatAM(Node *S, AMgroup &Map)                      //构造邻接矩阵
{
    cout << "输入邻接矩阵顶点数:";
    cin >> Map.point;
    cout << "输入邻接矩阵边数:";
    cin >> Map.vert;
    getchar();

    char a[100];
    cout << "输入点的信息:";
    gets_s(a);
    for (int i = 0; i < Map.point; i++)             //依次输入点的信息
    {
        Map.VTchart[i] = a[i];
    }

    char v1, v2; int len;
    for (int i = 0; i < Map.vert; i++)               //构造邻接矩阵的各个边存储到Node结构体里面
    {
        cout << "输入第" << i + 1 << "条边的两个顶点以及权值:";
        cin >> v1 >> v2 >> len;
        int m, n;
        m = AMlocate(Map, v1);
        n = AMlocate(Map, v2);

        S[i].Head = v1;
        S[i].Tail = v2;
        S[i].lowcost = len;
    }
    cout << "当前所有权情况:" << endl;
    for (int i = 0; i < Map.vert; i++)
    {
        cout << S[i].Head << "--" << S[i].Tail << "      " << S[i].lowcost << endl;
    }
    cout << endl;
}

//合并过程(治的过程)                                                                                  //归并排序过程
void Merge(Node *S, Node *S1, int start, int mid, int end)
{
    int i = start, j = mid + 1, k = start;
    while (i != mid + 1 && j != end + 1)
    {
        if (S[i].lowcost > S[j].lowcost)
        {
            S1[k].lowcost = S[j].lowcost;
            S1[k].Head = S[j].Head;
            S1[k].Tail = S[j].Tail;
            k++; j++;
        }
        else
        {
            S1[k].lowcost = S[i].lowcost;
            S1[k].Head = S[i].Head;
            S1[k].Tail = S[i].Tail;
            k++; i++;
        }
    }
    while (i != mid + 1)
    {
        S1[k].lowcost = S[i].lowcost;
        S1[k].Head = S[i].Head;
        S1[k].Tail = S[i].Tail;
        k++; i++;
    }
    while (j != end + 1)
    {
        S1[k].lowcost = S[j].lowcost;
        S1[k].Head = S[j].Head;
        S1[k].Tail = S[j].Tail;
        k++; j++;
    }
    for (i = start; i <= end; i++)
    {
        S[i].lowcost = S1[i].lowcost;
        S[i].Head = S1[i].Head;
        S[i].Tail = S1[i].Tail;
    }
}

//递归部分(分的部分)                                                                              //归并排序过程
void MergeSort(Node *S, Node *S1, int start, int end)
{
    int mid;
    if (start < end)
    {
        mid = start + (end - start) / 2;
        MergeSort(S, S1, start, mid);
        MergeSort(S, S1, mid + 1, end);
        Merge(S, S1, start, mid, end);
    }
}

void Sort(Node *S, AMgroup &Map)      //将数组中的权值排序
{
    struct Node S1[30];
    MergeSort(S, S1, 0, Map.vert - 1);
}

void MST_Kruskal(struct AMgroup &Map, struct Node *S)    //克鲁斯卡算法
{
    int v1, v2;
    int vs1, vs2;
    Sort(S, Map);                                    //将元素按权值从小到大排序
    cout << "排序后权值:" << endl;
    for (int i = 0; i < Map.vert; i++)
    {
        cout << S[i].Head << "--";
        cout << S[i].Tail << "      ";
        cout << S[i].lowcost << endl;                
    }
    cout << endl;

    for (int i = 0; i < Map.point; i++)                     //将所有顶点的Vexset赋值为本身
    {
        Vexset[i] = i;
    }
    for (int i = 0; i < Map.vert; i++)                           //遍历所有边
    {
        for (int p = 0; p < Map.point; p++) 
        {
            cout << Vexset[p] << "  ";
        }
        cout << endl;
        v1 = AMlocate(Map, S[i].Head);                          //找到最小边的两个顶点
        v2 = AMlocate(Map, S[i].Tail);
        vs1 = Vexset[v1];                                               //两个顶点的Vexset值
        vs2 = Vexset[v2];
        if (vs1 != vs2)                                                                     //如果不等代表不连通
        {
            cout << S[i].Head << "->" << S[i].Tail << endl;
            for (int j = 0; j < Map.vert; j++)                                       //通过遍历将将要加入的所有顶点都赋值为一样                      
            {
                if (Vexset[j] == vs2)
                {
                    Vexset[j] = vs1;
                }
            }
        }
    }
}

int main()
{
    struct AMgroup Jzhen[30];       //邻接矩阵
    struct Node Szu[30];               //边的数组
    CreatAM(Szu, *Jzhen);
    MST_Kruskal(*Jzhen, Szu);
    system("pause");
    return 0;
}