数据结构之最小生成树Kruskal算法建议收藏

1.克鲁斯卡算法介绍克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路。具体做法:首先构造一个

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

全栈程序员社区此处内容已经被作者隐藏,请输入验证码查看内容
验证码:
请关注本站微信公众号,回复“验证码”,获取验证码。在微信里搜索“全栈程序员社区”或者“www_javaforall_cn”或者微信扫描右侧二维码都可以关注本站微信公众号。

1. 克鲁斯卡算法介绍

  克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。

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

2. 克鲁斯卡算法图解

数据结构之最小生成树Kruskal算法建议收藏

第1步:将边<E,F>加入R中。     

  边<E,F>的权值最小,因此将它加入到最小生成树结果R中。

第2步:将边<C,D>加入R中。     

  上一步操作之后,边<C,D>的权值最小,因此将它加入到最小生成树结果R中。

第3步:将边<D,E>加入R中。     

  上一步操作之后,边<D,E>的权值最小,因此将它加入到最小生成树结果R中。

第4步:将边<B,F>加入R中。     

  上一步操作之后,边<C,E>的权值最小,但<C,E>会和已有的边构成回路;因此,跳过边<C,E>。同理,跳过边<C,F>。将边<B,F>加入到最小生成树结果R中。

第5步:将边<E,G>加入R中。     

  上一步操作之后,边<E,G>的权值最小,因此将它加入到最小生成树结果R中。

第6步:将边<A,B>加入R中。     

  上一步操作之后,边<F,G>的权值最小,但<F,G>会和已有的边构成回路;因此,跳过边<F,G>。同理,跳过边<B,C>。将边<A,B>加入到最小生成树结果R中。

此时,最小生成树构造完成!它包括的边依次是:<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>

3. 代码实现

(1)根据图链表顶点信息得到所有边信息

EData* listUDG::GetEdage()
{
    EData *pEdata = new EData[m_nEdgNum];
    ENode *pTemp = NULL;
    int nEdgNum = 0;
    for (int i = 0; i < m_nVexNum; i ++)
    {
        pTemp = m_mVexs[i].pFirstEdge;
        while(pTemp != NULL)
        {
            if (pTemp->nVindex > i) // 可以不需要改代码,但是为了严谨和效率
            {
                pEdata[nEdgNum].nStart = m_mVexs[i].data;
                pEdata[nEdgNum].nEnd = m_mVexs[pTemp->nVindex].data;
                pEdata[nEdgNum].nWeight = pTemp->nWeight;

                nEdgNum ++;
            }
            pTemp = pTemp->pNext;
        }
    }

    return pEdata;
}

(2)对所有边根据权重值大小进行排序

void listUDG::SortEdges(EData* edges, int elen) 
{ 
    int i,j; 
    for (i=0; i<elen; i++) 
    { 
        for (j=i+1; j<elen; j++) 
        { 
            if (edges[i].nWeight > edges[j].nWeight) 
            { 
                // 交换"边i"和"边j" 
                swap(edges[i], edges[j]); 
            } 
        } 
    } 
} 

(3)判断选取的边是否构成回路

  若0->1->2->3->4   5->3

  则a[0]=1;a[1]=2;a[2]=3;a[3]=4;

   a[5]=3;

  因为a[5]和a[2]的尾顶点都是3,则构成回路

int listUDG::GetEnd(int *vends, int i)
{
    while(vends[i] != 0)
    {
        i = vends[i];
    }

    return i;
}

(4)Kruskal算法

// kruska最小生成树
void listUDG::Kruskal()
{
    // 得到所有的边
    EData *pEdata = GetEdage();
    // 根据边的权重进行排序
    SortEdges(pEdata,m_nEdgNum);
    int vends[MAX] = {0};     // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
    EData rets[MAX];      // 结果数组,保存kruskal最小生成树的边  
    int nStartIndex,nEndIndex;
    int nIndex = 0;
    int m,n;
    for (int i = 0; i < m_nEdgNum; i ++)
    {
        nStartIndex = GetVIndex(pEdata[i].nStart);
        nEndIndex = GetVIndex(pEdata[i].nEnd);

        m = GetEnd(vends, nStartIndex);
        n = GetEnd(vends, nEndIndex);
        if (m != n)
        {
            vends[m] = n;                // 设置m在"已有的最小生成树"中的终点为n
            rets[nIndex++] = pEdata[i];           // 保存结果
        }
    }

    delete[] pEdata;

    int nSum = 0; 
    for (int i = 0; i < nIndex; i++) 
        nSum += rets[i].nWeight; 
    cout << "Kruskal=" << nSum << ": "; 
    for (int i = 0; i < nIndex; i++) 
        cout << "(" << rets[i].nStart << "," << rets[i].nEnd << ") "; 
    cout << endl; 
}

(5)全部代码

数据结构之最小生成树Kruskal算法建议收藏
数据结构之最小生成树Kruskal算法建议收藏

#include "stdio.h"
#include <iostream>
using namespace std;

#define MAX 100
#define INF         (~(0x1<<31))        // 最大值(即0X7FFFFFFF)

class EData
{
public:
    EData(){}
    EData(char start, char end, int weight) : nStart(start), nEnd(end), nWeight(weight){}
    
    char nStart;
    char nEnd;
    int nWeight;
};
//
struct ENode
{
    int nVindex;  // 该边所指的顶点的位置
    int nWeight;  // 边的权重
    ENode *pNext; // 指向下一个边的指针
};

struct VNode
{
    char data;  // 顶点信息
    ENode *pFirstEdge; // 指向第一条依附该顶点的边
};

// 无向邻接表
class listUDG
{
public:
    listUDG();
    listUDG(char *vexs, int vlen, EData **pEData, int elen);
    ~listUDG();

    void PrintUDG();
    // Prim最小生成树
    void Prim(int nStart);
    // 得到所有的边
    EData* GetEdage();
    // kruska最小生成树
    void Kruskal();
private:
    // 获取<start, end>的权值,若start和end不是连接的,则返回无穷大
    int GetWeight(int start, int end);
    // 返回顶点的索引
    int GetVIndex(char ch);
    void LinkLast(ENode *pFirstNode, ENode *pNode);
    void QuickSort(EData* pEdata, int nEdgNum);
    void SortEdges(EData* edges, int elen);
    int GetEnd(int *vends, int i);
private:
    int m_nVexNum;   // 顶点数目
    int m_nEdgNum;   // 边数目
    VNode m_mVexs[MAX];
    VNode m_PrimVexs[MAX];
};

listUDG::listUDG()
{

}
listUDG::listUDG(char *vexs, int vlen, EData **pEData, int elen)
{
    m_nVexNum = vlen;
    m_nEdgNum = elen;

    // 初始化"邻接表"的顶点
    for (int i = 0; i < vlen; i ++)
    {
        m_mVexs[i].data = vexs[i];
        m_mVexs[i].pFirstEdge = NULL;
    }

    char c1,c2;
    int p1,p2;
    ENode *node1, *node2;
    // 初始化"邻接表"的边
    for (int j = 0; j < elen; j ++)
    {
        // 读取边的起始顶点和结束顶点
        c1 = pEData[j]->nStart;
        c2 = pEData[j]->nEnd;
        p1 = GetVIndex(c1);
        p2 = GetVIndex(c2);

        node1 = new ENode();
        node1->nVindex = p2;
        node1->nWeight = pEData[j]->nWeight;
        if (m_mVexs[p1].pFirstEdge == NULL)
        {
            m_mVexs[p1].pFirstEdge = node1;
        }
        else
        {
            LinkLast(m_mVexs[p1].pFirstEdge, node1);
        }

        node2 = new ENode();
        node2->nVindex = p1;
        node2->nWeight = pEData[j]->nWeight;
        if (m_mVexs[p2].pFirstEdge == NULL)
        {
            m_mVexs[p2].pFirstEdge = node2;
        }
        else
        {
            LinkLast(m_mVexs[p2].pFirstEdge, node2);
        }
    }

}
listUDG::~listUDG()
{
    ENode *pENode = NULL;
    ENode *pTemp = NULL;
    for (int i = 0; i < m_nVexNum; i ++)
    {
        pENode = m_mVexs[i].pFirstEdge;
        if (pENode != NULL)
        {
            pTemp = pENode;
            pENode = pENode->pNext;

            delete pTemp;
        }
        delete pENode;
    }
}

void listUDG::PrintUDG()
{ 
    ENode *pTempNode = NULL;
    cout << "邻接无向表:" << endl;
    for (int i = 0; i < m_nVexNum; i ++)
    {
        cout << "顶点:" << GetVIndex(m_mVexs[i].data)<< "-" << m_mVexs[i].data<< "->";
        pTempNode = m_mVexs[i].pFirstEdge;
        while (pTempNode)
        {
            cout <<pTempNode->nVindex << "->";
            pTempNode = pTempNode->pNext;
        }
        cout << endl;
    }
}

// Prim最小生成树
void listUDG::Prim(int nStart)
{
    int i = 0;
    int nIndex=0;         // prim最小树的索引,即prims数组的索引 
    char cPrims[MAX];     // prim最小树的结果数组 
    int weights[MAX];    // 顶点间边的权值

    cPrims[nIndex++] = m_mVexs[nStart].data;

    // 初始化"顶点的权值数组",
    // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
    for (i = 0; i < m_nVexNum; i++)
    {
        weights[i] = GetWeight(nStart, i);
    }

    for (i = 0; i < m_nVexNum; i ++)
    {
        if (nStart == i)
        {
            continue;
        }

        int min = INF;
        int nMinWeightIndex = 0;
        for (int k = 0; k < m_nVexNum; k ++)
        {
            if (weights[k]!= 0 && weights[k] < min)
            {
                min = weights[k];
                nMinWeightIndex = k;
            }
        }

        // 找到下一个最小权重值索引
        cPrims[nIndex++] = m_mVexs[nMinWeightIndex].data;
        // 以找到的顶点更新其他点到该点的权重值
        weights[nMinWeightIndex]=0;
        int nNewWeight = 0;
        for (int ii = 0; ii < m_nVexNum; ii++)
        {
            nNewWeight = GetWeight(nMinWeightIndex, ii);
            // 该位置需要特别注意
            if (0 != weights[ii] && weights[ii] > nNewWeight)
            {
                weights[ii] = nNewWeight;
            }
        }
    }
    // 计算最小生成树的权重值
    int nSum = 0;
    for (i = 1; i < nIndex; i ++)
    {
        int min = INF;
        int nVexsIndex = GetVIndex(cPrims[i]);
        for (int kk = 0; kk < i; kk ++)
        {
            int nNextVexsIndex = GetVIndex(cPrims[kk]);
            int nWeight = GetWeight(nVexsIndex, nNextVexsIndex);
            if (nWeight < min)
            {
                min = nWeight;
            }
        }
        nSum += min;
    }

    // 打印最小生成树 
    cout << "PRIM(" << m_mVexs[nStart].data  <<")=" << nSum << ": "; 
    for (i = 0; i < nIndex; i++) 
        cout << cPrims[i] << " "; 
    cout << endl; 
}

// 得到所有的边
EData* listUDG::GetEdage()
{
    EData *pEdata = new EData[m_nEdgNum];
    ENode *pTemp = NULL;
    int nEdgNum = 0;
    for (int i = 0; i < m_nVexNum; i ++)
    {
        pTemp = m_mVexs[i].pFirstEdge;
        while(pTemp != NULL)
        {
            if (pTemp->nVindex > i) // 可以不需要改代码,但是为了严谨和效率
            {
                pEdata[nEdgNum].nStart = m_mVexs[i].data;
                pEdata[nEdgNum].nEnd = m_mVexs[pTemp->nVindex].data;
                pEdata[nEdgNum].nWeight = pTemp->nWeight;

                nEdgNum ++;
            }
            pTemp = pTemp->pNext;
        }
    }

    return pEdata;
}
// 根据权重值对所有的边进行排序
void listUDG::SortEdges(EData* edges, int elen) 
{ 
    int i,j; 
    for (i=0; i<elen; i++) 
    { 
        for (j=i+1; j<elen; j++) 
        { 
            if (edges[i].nWeight > edges[j].nWeight) 
            { 
                // 交换"边i"和"边j" 
                swap(edges[i], edges[j]); 
            } 
        } 
    } 
} 

int listUDG::GetEnd(int *vends, int i)
{
    while(vends[i] != 0)
    {
        i = vends[i];
    }

    return i;
}
// kruska最小生成树
void listUDG::Kruskal()
{
    // 得到所有的边
    EData *pEdata = GetEdage();
    // 根据边的权重进行排序
    SortEdges(pEdata,m_nEdgNum);
    int vends[MAX] = {0};     // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
    EData rets[MAX];      // 结果数组,保存kruskal最小生成树的边  
    int nStartIndex,nEndIndex;
    int nIndex = 0;
    int m,n;
    for (int i = 0; i < m_nEdgNum; i ++)
    {
        nStartIndex = GetVIndex(pEdata[i].nStart);
        nEndIndex = GetVIndex(pEdata[i].nEnd);

        m = GetEnd(vends, nStartIndex);
        n = GetEnd(vends, nEndIndex);
        if (m != n)
        {
            vends[m] = n;                // 设置m在"已有的最小生成树"中的终点为n
            rets[nIndex++] = pEdata[i];           // 保存结果
        }
    }

    delete[] pEdata;

    int nSum = 0; 
    for (int i = 0; i < nIndex; i++) 
        nSum += rets[i].nWeight; 
    cout << "Kruskal=" << nSum << ": "; 
    for (int i = 0; i < nIndex; i++) 
        cout << "(" << rets[i].nStart << "," << rets[i].nEnd << ") "; 
    cout << endl; 
}
// 获取<start, end>的权值,若start和end不是连接的,则返回无穷大
int listUDG::GetWeight(int start, int end)
{
    if (start == end)
    {
        return 0;
    }
    ENode *pTempNode = m_mVexs[start].pFirstEdge;
    while (pTempNode)
    {
        if (end == pTempNode->nVindex)
        {
            return pTempNode->nWeight;
        }
        pTempNode = pTempNode->pNext;
    }

    return INF;
}

// 返回顶点的索引
int listUDG::GetVIndex(char ch)
{
    int i = 0;
    for (; i < m_nVexNum; i ++)
    {
        if (m_mVexs[i].data == ch)
        {
            return i;
        }
    }
    return -1;
}

void listUDG::LinkLast(ENode *pFirstNode, ENode *pNode)
{
    if (pFirstNode == NULL || pNode == NULL)
    {
        return;
    }
    ENode *pTempNode = pFirstNode;
    while (pTempNode->pNext != NULL)
    {
        pTempNode = pTempNode->pNext;
    }

    pTempNode->pNext = pNode;
}


void main()
{
    char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; 
    //
    EData *edges[] = { 
               // 起点 终点 权 
        new EData('A', 'B', 12),  
        new EData('A', 'F', 16),  
        new EData('A', 'G', 14),  
        new EData('B', 'C', 10),  
        new EData('B', 'F',  7),  
        new EData('C', 'D',  3),  
        new EData('C', 'E',  5),  
        new EData('C', 'F',  6),  
        new EData('D', 'E',  4),  
        new EData('E', 'F',  2),  
        new EData('E', 'G',  8),  
        new EData('F', 'G',  9)
    };
    int vlen = sizeof(vexs)/sizeof(vexs[0]); 
    int elen = sizeof(edges)/sizeof(edges[0]); 
    listUDG* pG = new listUDG(vexs, vlen, edges, elen); 

    pG->PrintUDG();   // 打印图 
    pG->Prim(0);
    pG->Kruskal();
    for (int i = 0; i < elen; i ++)
    {
        delete edges[i];
    }
    return; 
}

View Code

数据结构之最小生成树Kruskal算法建议收藏

注:感谢
skywang12345博主提供的内容分析,要了解更多详细信息可参考原博
http://www.cnblogs.com/skywang12345/p/3711500.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/120147.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • adb shell getenforce/setenforce(三级命令)

    adb shell getenforce/setenforce(三级命令)adbshellgetenforceadbshellsetenforce

    2022年6月27日
    35
  • linux 虚拟ip 漂移,keepalived 虚拟ip切换

    linux 虚拟ip 漂移,keepalived 虚拟ip切换简介 KeepalivedKe 是运行在 lvs 之上 是一个用于做双机热备 HA 的软件 它的主要功能是实现真实机的故障隔离及负载均衡器间的失败切换 提高系统的可用性 虚拟 ip 虚 IP 就是一个未分配给真实主机的 IP 也就是说对外提供数据库服务器的主机除了有一个真实 IP 外还有一个虚 IP 使用这两个 IP 中的任意一个都可以连接到这台主机 所有项目中数据库链接一项配置的都是这个虚 IP

    2025年8月2日
    5
  • 视频压缩算法的相关知识(转载)

    视频压缩算法的相关知识(转载)视频压缩算法的相关知识视频压缩的基本原理,一些常见压缩算法的概念视频压缩算法的相关知识视频压缩基本概念视频压缩算法的相关知识MPEG-1MPEG视频压缩编码后包括三种元素:I帧(I-fram

    2022年7月2日
    22
  • MySQL主从复制配置[通俗易懂]

    MySQL主从复制配置[通俗易懂]MySQL主从复制及读写分离一、MySQL复制的应用常见场景 读写分离,提高查询访问性能,有效减少主数据库访问压力。 实时灾备,主数据库出现故障时,可快速切换到从数据库。 数据汇总,可将多个主数据库同步汇总到一个数据库中,方便数据统计分析。 二、MySQL主从复制原理介绍1、MySQL异步和半同步复制传统的MySQL复制提供了一种简单的主-从复制方法。有一个主,以及一个或多个从。主节点执行和提交事务,然后将它们(异步地)发送到从节点,以重新…

    2022年8月13日
    5
  • 【Java】JVM垃圾回收机制与类加载机制

    【Java】JVM垃圾回收机制与类加载机制不同于C++需要编程人员手动释放内存,Java有虚拟机,因此Java不需要程序员主动去释放内存,而是通过虚拟机自身的垃圾回收器(GarbageCollector-GC)来进行对象的回收。Java语言由于有虚拟机的存在,实现了平台无关性,在任意平台都是通过将代码转换为字节码文件,从而在平台下的虚拟机中运行代码的。JVM内存区域分布虚拟机栈:存放每个方法执行时的栈帧,一个方法调用到…

    2022年5月18日
    39
  • springcloud学习(三)之Hystrix

    springcloud学习(三)之Hystrix前言雪崩效应在微服务架构中,⼀个应⽤可能会有多个微服务组成,微服务之间的数据交互通过远程过程调⽤完成。这就带来⼀个问题,假设微服务A调⽤微服务B和微服务C,微服务B和微服务C⼜调⽤其它的微服务,

    2022年7月4日
    22

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号