克鲁斯卡尔(kruskal)算法

  • kruskal 用于生成最小生成树
  • 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路。
  • 具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止。


  • 实现代码
#include<iostream>
#include<string.h>
using namespace std;

#define len 100

// 边的结构体
class EData{
    public:
        char start;  // 边的起点
        char end;    // 边的终点
        int weight;  // 边的权重

        EData(){

        }
        EData(char s, char e, int w){
            start  = s;
            end    = e;
            weight = w;
        }
};

class Graph{
    // 内部类
    private:
        // 邻接表中表对应的链表的顶点
        class ENode{
            public:
                int vex;         // 顶点
                int weight;      // 权重 
                ENode *nextEdge; // 指向下一条弧
        };

        // 邻接表中表的顶点
        class VNode{
            public:
                char data; // 顶点信息
                ENode *firstEdge; // 指向第一条依付该顶点的弧
        };

    // 私有成员
    private:
        int n;              // 节点个数 
        int e;              // 边的个数
        VNode mVexs[len];
    
    public:
        Graph(){
            ENode *node1, *node2;
            n = 7; 
            e = 12;

            // 设置节点为默认数值
            string nodes = "ABCDEFG";
            // 输入节点
            for(int i=0; i < n; i++){
                mVexs[i].data = nodes[i];
                mVexs[i].firstEdge = NULL;
            }

            // 设置边为默认值
            char edges[][2] = {
                {'A', 'B'}, 
                {'A', 'F'}, 
                {'A', 'G'}, 
                {'B', 'C'}, 
                {'B', 'F'}, 
                {'C', 'D'}, 
                {'C', 'E'},
                {'C', 'F'}, 
                {'D', 'E'},
                {'E', 'F'}, 
                {'E', 'G'},
                {'F', 'G'}
            };
            
            // 边的权重
            int weights[len] = {12, 16, 14, 10, 7, 3, 5, 6, 4, 2, 8, 9};
            
            // 初始化邻接表的边
            for(int i=0; i < e; i++){
                int start = get_Node_Index(edges[i][0]);
                int end = get_Node_Index(edges[i][1]);

                // 初始化 node1
                node1 = new ENode();
                node1->vex = end;
                node1->weight = weights[i];
                node1->nextEdge = NULL;
                // 将 node 添加到 srart 所在链表的末尾
                if(mVexs[start].firstEdge == NULL){
                    mVexs[start].firstEdge = node1;
                }
                else{
                    linkLast(mVexs[start].firstEdge, node1);
                }

                // 初始化 node2
                node2 = new ENode();
                node2->vex = start;
                node2->weight = weights[i];
                node2->nextEdge = NULL;
                // 将 node 添加到 end 所在链表的末尾
                if(mVexs[end].firstEdge == NULL){
                    mVexs[end].firstEdge = node2;
                }
                else{
                    linkLast(mVexs[end].firstEdge, node2);
                }
            }
        }

        void linkLast(ENode*p1, ENode*p2){
            ENode*p = p1;
            while(p->nextEdge){
                p = p->nextEdge;
            }
            p->nextEdge = p2;
        }
        
        // 返回顶点下标
        int get_Node_Index(char number){
            for(int i=0; i < n; i++){
                if(number == mVexs[i].data){
                    return i;
                }
            }
            return -1; //这句话永远不会执行的
        }

        // 输出邻接表
        void print(){
            for(int i=0; i < n; i ++){
                cout<<mVexs[i].data;
                ENode *temp = mVexs[i].firstEdge;
                while(temp){
                    cout<<" -> "<<temp->vex;
                    temp = temp->nextEdge;
                }
                cout<<endl;
            }
            cout<<endl;
        }
        
        // 获取图中的边
        EData* get_Edge(){
            EData *edges;
            ENode *node;
            int index = 0;

            edges = new EData[e];
            for(int i=0; i < n; i++){
                node = mVexs[i].firstEdge;
                while(node){
                    if(node->vex > i){
                        edges[index].start  = mVexs[i].data;
                        edges[index].end    = mVexs[node->vex].data;
                        edges[index].weight = node->weight;
                        index ++;
                    }
                    node = node->nextEdge;
                }
            }
            return edges;
        }

        // 对边依据权重大小进行排序 
        // 此处采用 冒泡排序
        void sort_Edge(EData *edges){
            EData temp;
            for(int i = e-1; i > 0; i--){
                for(int j=0; j < i; j++){
                    if(edges[j].weight > edges[j+1].weight){
                        temp = edges[j];
                        edges[j] = edges[j+1];
                        edges[j+1] = temp;
                    }
                }
            }
        }
        
        // 获取节点的'终点'
        int get_End(int vends[], int i){
            while(vends[i] != 0){
                i = vends[i];
            }
            return i;
        }

        // 克鲁斯卡尔算法用于生成最小生成树
        void kruskal(){
            int index = 0;
            EData *all_Edge; // 存放全部的边
            
            int vends[len];    // 保存最小生成树中每个节点的 '终点'
            memset(vends, 0, len);
            
            EData *ret;      // 存放克鲁斯卡尔生成最小生成树的边
            ret = new EData[e-1];

            // 获取图中的边
            all_Edge = get_Edge();

            // 对边依据权重大小进行排序
            sort_Edge(all_Edge);

            for(int i=0; i < e; i++){
                int p1 = get_Node_Index(all_Edge[i].start);
                int p2 = get_Node_Index(all_Edge[i].end);

                int m = get_End(vends, p1);
                int n = get_End(vends, p2);

                if(m != n){
                    vends[m] = n;
                    ret[index++] = all_Edge[i];
                }
            }

            delete[] all_Edge;

            // 统计并打印 kruskal 最小生成树的信息
            int length = 0;
            for(int i = 0; i < index; i++){
                length += ret[i].weight;
            }
            cout<<"Kruskal 最小生成树的长度为: "<<length<<endl;
            for(int i=0; i < index; i++){
                cout<< "("<<ret[i].start <<"," << ret[i].end<<")"<<endl;
            }
        }
};

int main(){
    Graph g;
    // 输出邻接表
    // g.print();

    g.kruskal();
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容