码迷,mamicode.com
首页 > 编程语言 > 详细

求最小生成树——Kruskal算法

时间:2017-10-26 20:02:02      阅读:228      评论:0      收藏:0      [点我收藏+]

标签:ima   clu   void   int   get   自己   bsp   ace   har   

给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法。这篇文章先介绍Kruskal算法。

Kruskal算法的基本思想:先将所有边按权值从小到大排序,然后按顺序选取每条边,假如一条边的两个端点不在同一个集合中,就将这两个端点合并到同一个集合中;假如两个端点在同一个集合中,说明这两个端点已经连通了,就将当前这条边舍弃掉;当所有顶点都在同一个集合时,说明最小生成树已经形成。(写代码的时候会将所有边遍历一遍)

 

来看一个例子:

技术分享

步骤:

(1)先根据权值把边排序:

AD 5

CE 5

DF 6

AB 7

BE 7

BC 8

EF 8

BD 9

EG 9

FG 11

 

(2)

选择AD这条边,将A、D加到同一个集合1中

选择CE这条边,将C、E加到同一个集合2中(不同于AD的集合)

选择DF这条边,由于D已经在集合1中,因此将F加入到集合1中,集合变为A、D、F

选择AB这条边,同理,集合1变为A、B、D、F

选择BE这条边,由于B在集合1中,E在集合2中,因此将两个集合合并,形成一个新的集合ABCDEF

由于E、F已经在同一集合中,舍弃掉BC这条边;同理舍弃掉EF、BD

选择EG这条边,此时所有元素都已经在同一集合中,最小生成树形成

象征性地舍弃掉FG这条边

 

 

实现代码如下:

#include <iostream>
#include <cstring>
#define MaxSize 20
using namespace std;

struct Edge{
    int begin;     
    int end;     
    int weight;  
}; 
struct Graph{
    char ver[MaxSize + 1];
    int edg[MaxSize][MaxSize];
};

void CreateGraph(Graph *g) {
    int VertexNum;
    char Ver;
    int i = 0;
    cout << "输入图的顶点:" << endl;
    while ((Ver = getchar()) != \n) {
        g->ver[i] = Ver;
        i++;
    }
    g->ver[i] = \0;
    VertexNum = strlen(g->ver);
    cout << "输入相应的邻接矩阵" << endl;
    for (int i = 0; i < VertexNum; i++) {
        for (int j = 0; j < VertexNum; j++) {
            cin >> g->edg[i][j];    //输入0则为没有边相连啊
        }
    }
}

void PrintGraph(Graph g) {
    int VertexNum = strlen(g.ver);
    cout << "图的顶点为:" << endl;
    for (int i = 0; i < VertexNum; i++) {
        cout << g.ver[i] << " ";
    }
    cout << endl;
    cout << "图的邻接矩阵为:" << endl;
    for (int i = 0; i < VertexNum; i++) {
        for (int j = 0; j < VertexNum; j++) {
            cout << g.edg[i][j] << " ";
        }
        cout << endl;
    }
}

int getVerNum(Graph g) {
    return strlen(g.ver);
}

int getEdgeNum(Graph g) {
    int res = 0;
    int VertexNum = getVerNum(g);
    for (int i = 0; i < VertexNum; i++) {
        //邻接矩阵对称,计算上三角元素和即可
        for (int j = i + 1 /*假设没有自己指向自己的*/; j < VertexNum; j++) {
            if (g.edg[i][j] != 0) res++;
        }
    }    
    return res;
}

Edge *CreateEdges(Graph g) {
    int k = 0;
    int EdgeNum = getEdgeNum(g);
    int VertexNum = getVerNum(g);
    Edge * p = new Edge[EdgeNum];
    for (int i = 0; i < VertexNum; i++) {
        for (int j = i; j < VertexNum; j++) {
            if (g.edg[i][j] != 0) {
                p[k].begin = i;
                p[k].end = j;
                p[k].weight = g.edg[i][j];
                k++;
            }
        }
    }
    for (int i = 0; i < EdgeNum - 1; i++) {
        Edge minWeightEdge = p[i];
        for (int j = i + 1; j < EdgeNum; j++) {
            if (minWeightEdge.weight > p[j].weight) {
                Edge temp = minWeightEdge;
                minWeightEdge = p[j];
                p[j] = temp;
            }
        }
        p[i] = minWeightEdge;
    }
    return p;
}

void Kruskal(Graph g) {
    int VertexNum = getVerNum(g);
    int EdgeNum = getEdgeNum(g);
    Edge *p = CreateEdges(g);
    int *index = new int[VertexNum];    //index数组,其元素为连通分量的编号,index[i]==index[j]表示编号为i和j的顶点在同一连通分量中
    int *MSTEdge = new int[VertexNum - 1];    //用来存储已确定的最小生成树的**边的编号**,共VertexNum-1条边
    int k = 0;
    int WeightSum = 0;
    int IndexBegin, IndexEnd;
    for (int i = 0; i < VertexNum; i++) {
        index[i] = -1;    //初始化所有index为-1
    }
    for (int i = 0; i < VertexNum - 1; i++) {
        for (int j = 0; j < EdgeNum; j++) {
            if ( !(index[p[j].begin] >= 0 && index[p[j].end] >= 0 && index[p[j].begin] == index[p[j].end] /*若成立表明p[j].begin和p[j].end已在同一连通块中(且可相互到达,废话)*/) ) {
                MSTEdge[i] = j;
                if (index[p[j].begin] == -1 && index[p[j].end] == -1) {
                    index[p[j].begin] = index[p[j].end] = i;
                }
                else if (index[p[j].begin] == -1 && index[p[j].end] >= 0) {
                    index[p[j].begin] = i;
                    IndexEnd = index[p[j].end];
                    for (int n = 0; n < VertexNum; n++) {
                        if (index[n] == IndexEnd) {
                            index[n] == i;
                        }
                    }
                }
                else if (index[p[j].begin] >= 0 && index[p[j].end] == -1) {
                    index[p[j].end] = i;
                    IndexBegin = index[p[j].begin];
                    /*将连通分量合并(或者说将没加入连通分量的顶点加进去,然后将原来连通分量的值改了)*/
                    for (int n = 0; n < VertexNum; n++) {
                        if (index[n] == IndexBegin) {
                            index[n] == i;
                        }
                    }
                }
                else {
                    IndexBegin = index[p[j].begin];
                    IndexEnd = index[p[j].end];
                    for (int n = 0; n < VertexNum; n++) {
                        if (index[n] == IndexBegin || index[n] == IndexEnd) {
                            index[n] = i;
                         }
                    }
                }
                break;
            }
        }
    }
    cout << "MST的边为:" << endl;
    for (int i = 0; i < VertexNum - 1; i++) {
        cout << g.ver[p[MSTEdge[i]].begin] << "--" << g.ver[p[MSTEdge[i]].end] << endl;
        WeightSum += p[MSTEdge[i]].weight;
    }
    cout << "MST的权值为:" << WeightSum << endl;
}

 

求最小生成树——Kruskal算法

标签:ima   clu   void   int   get   自己   bsp   ace   har   

原文地址:http://www.cnblogs.com/fengziwei/p/7738389.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!