导航

kruskal算法

Posted on 2016-10-09 19:55  CSU蛋李  阅读(257)  评论(0编辑  收藏  举报

4克鲁斯卡尔算法—集合运算应用

 

 

 

 

 

上图表述了生成树的算法过程

(选最短边,不构成回路)

只要有交集就合并

“原图”的数据对象可以使用邻接表来表达:

IntgData[7][7]

 

 

“边长排序图”的数据可以用线性表保存,数据结构:

算法描术:

算法过程,本质上是集合操作,集合的初始状态是空集,然后:

1)点对(1,3)进入集合Vertex[ ]={ {1,3}  }   集合里有一个子集,将对应的边选入线性表(  Vertex[7 ]={ 0,0,0,0,0,0,0})

2) 点对(4,6)进入集合Vertex={ {1,3},{4,6} }   集合里有两个子集,将对应的边选入线性表。

3)点对(2,5)进入集合Vertex={ {1,3},{4,6},{2,5} }   集合里有三个子集,将对应的边选入线性表。

4)点对(3,6)进入集合Vertex={ {1,3},{4,6},{2,5},{3,6} },点对(3,6)与两个集合{1,3},{4,6}有交集,这时需要将它们变为一个集合,即进行集合并运算。最后集合变为Vertex={ {1,3,4,6},{2,5} },将对应的边选入线性表。

5)点对(1,4)不能进入集合,因为它是集合Vertex的子集,点对(1,4)的边会使生成树构成回路。

6)…依据上述规则,不断选入点对,对于有N个顶点的图,选N-1个点对(N-1条边)即可

总结:点对入选规则

1)不是子集且无交集,入选; 2)有交集,入选;合并相关集合;3)是子集,不入选。

 

从程序的观点看:

可以使用数组Vertex[]作为集合来描述顶点选入情况。Vertex[]的下标为顶点编号,值为该顶点是否被选。例Vertex[1]=0,表示顶点1不在集合中;Vertex[1]≠0表示顶点1在集合中。另外还需要一个集合编号,对应上述步骤,Vertex的值为:

1)Vertex[]={0,1,0,1,0,0,0}   下标——顶点编号

2)Vertex[]={0,1,0,1,2,0,2}

3)Vertex[]={0,1,3,1,2,3,2}

4)Vertex[]={0,1,3,1,1,3,1}

……

 

 

typedefstruct

{

    int  vtx1;   //点对与边长

    int  vtx2;

    int  length;

} dataSet;

 

typedefstruct

{

    dataSetVEdge[50];

    int count;

} sVEList, *LPListVE;

 

Vertex[N ]={ 0,0,0,0,0,0,0})

intgData[][N]={ { 0,0,0,0,0,0,0},

 

;

 

sVEList  LA={0},LB={0};

 

原始数据读入LA中

Int n=0;

For(i=1;i<=6;i++)

For(j=1;j<=6;j++)

{       if(gdata[i][j] && i<j)

   {   LA.VEdge[n].vtx1=I;

LA.VEdge[n].vtx1=j;

     LA.VEdge[n].length=gdata[i][j]

LA.count++

}

}

对LA排序

Int k=0   -指向LA头部+1

Int  m=1     集合标号

第一条边进入(1,3)

Vertex[N ]={ 0,1,0,1,0,0,0})

第一条边信息进入LB={0}

Vertex[N ]={ 0,1,0,1,2,0,2})  {Vertex[x1]== Vertex[x2]==0}

(通过m++实现)

第2条边信息进入LB={0}

// 克鲁斯卡尔2.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<functional>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

typedef struct
{
    unsigned v1;
    unsigned v2;
    unsigned weight;
}Edges;
typedef struct//顺序表定义
{
    vector<Edges> vec;
}Sqlist;

void initList(Sqlist &list, const vector<vector<int>> &a)//将顶点对、边长数据读入线性表
{
    size_t i, j;
    Edges temp_edges;
    for (i = 1;i<a.size();i++)
        for (j = 1;j<i;j++)
        {
            if (a[i][j])
            {
                if (i<j)       //保证(x,y)中x存放小的顶点数
                {
                    temp_edges.v1 = i;
                    temp_edges.v2 = j;
                }
                else
                {
                    temp_edges.v1 = j;
                    temp_edges.v2 = i;
                }
                temp_edges.weight = a[i][j];
                list.vec.push_back(temp_edges);
            }
        }
}

bool isShorter(const Edges &r_edg, const Edges &l_edg)
{
    return r_edg.weight < l_edg.weight;
}

void listSort(Sqlist &s)  //线性表按边长排序
{
    sort(s.vec.begin(), s.vec.end(), isShorter);
}

void printList(const Sqlist &s)//打印线性表
{

    for (size_t i = 0;i<s.vec.size();i++)
    {
        cout << "(" << s.vec[i].v1 << "-->" << s.vec[i].v2 << ",weight is " << s.vec[i].weight << ")" << '\t';
        if ((i + 1) % 2 == 0)
            cout << '\n';
    }
    cout << endl;
}
void kruskal()
{
    vector<vector<int>> IntgData= { { 0,0,0,0,0,0,0 },    //邻接矩阵创建初始化
    { 0,0,6,1,5,0,0 },
    { 0,6,0,5,0,3,0 },
    { 0,1,5,0,5,6,4 },
    { 0,5,0,5,0,0,2 },
    { 0,0,3,6,0,0,6 },
    { 0,0,0,4,2,6,0 }
    };
    int Vertex[7] = { 0 };    //初始化顶点集Vertex为空集

    int setNum = 1;        //集合编号

    int selectlocation = 0;    //所选点对在线性表A位置

    size_t seletarc = 1;//选择边的个数;
    Sqlist A;
    Sqlist B;
    //将顶点对、边长数据读入线性表A
    initList(A, IntgData);

    //线性表A按边长排序
    listSort(A);
    //printList(A);
    Edges temp_edges;
    while (seletarc<IntgData.size()-1)
    {
        //A中选一条边,根据他的两个顶点判断该边是否为生成树的边
        int x = A.vec[selectlocation].v1;
        int y = A.vec[selectlocation].v2;

        //顶点对是Vertex子集,取A中下一个点
        if (Vertex[x] == Vertex[y] && Vertex[x] != 0)
        {
            selectlocation++;
        }
        else
        {
            //顶点对与Vertex无交集,进入集合
            if (Vertex[x] == 0 && Vertex[y] == 0)
            {
                Vertex[x] = setNum;
                Vertex[y] = setNum;
                setNum++;
            }
            else if (Vertex[x] == 0 && Vertex[y] != 0)//顶点对与一个子集相交,没有交的进入到vertex中
            {
                Vertex[x] = Vertex[y];
            }
            else if (Vertex[x] != 0 && Vertex[y] == 0)
            {
                Vertex[y] = Vertex[x];
            }
            else if (Vertex[x] != 0 && Vertex[y] != 0)//顶点对与两个子集相交,相交的两个子集并运算
            {
                for (int i = 1;i<IntgData.size();i++)
                {
                    int temp = Vertex[y];
                    if (Vertex[i] == temp)
                        Vertex[i] = Vertex[x];//相邻点复制,取Vertex[y]的值覆盖所有Vertex[x]
                }
            }
            //所选的边进入线性表B
            temp_edges.v1 = x;
            temp_edges.v2 = y;
            temp_edges.weight = A.vec[selectlocation].weight;
            B.vec.push_back(temp_edges);
            seletarc++;
        }//else
    }//while
     //最后打印B集合
    cout << "最小生成树边为:" << endl;
    printList(B);
}
int main()
{
    kruskal();
    return 0;
}