1. 克鲁斯卡算法介绍

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

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

2. 克鲁斯卡算法图解

第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 = ;
for (int i = ; 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=; i<elen; i++)
{
for (j=i+; 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] != )
{
i = vends[i];
} return i;
}

(4)Kruskal算法

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

(5)全部代码

#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 = ; 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 = ; 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 = ; 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 = ; 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 = ;
int nIndex=; // prim最小树的索引,即prims数组的索引
char cPrims[MAX]; // prim最小树的结果数组
int weights[MAX]; // 顶点间边的权值 cPrims[nIndex++] = m_mVexs[nStart].data; // 初始化"顶点的权值数组",
// 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
for (i = ; i < m_nVexNum; i++)
{
weights[i] = GetWeight(nStart, i);
} for (i = ; i < m_nVexNum; i ++)
{
if (nStart == i)
{
continue;
} int min = INF;
int nMinWeightIndex = ;
for (int k = ; k < m_nVexNum; k ++)
{
if (weights[k]!= && weights[k] < min)
{
min = weights[k];
nMinWeightIndex = k;
}
} // 找到下一个最小权重值索引
cPrims[nIndex++] = m_mVexs[nMinWeightIndex].data;
// 以找到的顶点更新其他点到该点的权重值
weights[nMinWeightIndex]=;
int nNewWeight = ;
for (int ii = ; ii < m_nVexNum; ii++)
{
nNewWeight = GetWeight(nMinWeightIndex, ii);
// 该位置需要特别注意
if ( != weights[ii] && weights[ii] > nNewWeight)
{
weights[ii] = nNewWeight;
}
}
}
// 计算最小生成树的权重值
int nSum = ;
for (i = ; i < nIndex; i ++)
{
int min = INF;
int nVexsIndex = GetVIndex(cPrims[i]);
for (int kk = ; 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 = ; i < nIndex; i++)
cout << cPrims[i] << " ";
cout << endl;
} // 得到所有的边
EData* listUDG::GetEdage()
{
EData *pEdata = new EData[m_nEdgNum];
ENode *pTemp = NULL;
int nEdgNum = ;
for (int i = ; 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=; i<elen; i++)
{
for (j=i+; 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] != )
{
i = vends[i];
} return i;
}
// kruska最小生成树
void listUDG::Kruskal()
{
// 得到所有的边
EData *pEdata = GetEdage();
// 根据边的权重进行排序
SortEdges(pEdata,m_nEdgNum);
int vends[MAX] = {}; // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
EData rets[MAX]; // 结果数组,保存kruskal最小生成树的边
int nStartIndex,nEndIndex;
int nIndex = ;
int m,n;
for (int i = ; 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 = ;
for (int i = ; i < nIndex; i++)
nSum += rets[i].nWeight;
cout << "Kruskal=" << nSum << ": ";
for (int i = ; 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 ;
}
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 = ;
for (; i < m_nVexNum; i ++)
{
if (m_mVexs[i].data == ch)
{
return i;
}
}
return -;
} 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', ),
new EData('A', 'F', ),
new EData('A', 'G', ),
new EData('B', 'C', ),
new EData('B', 'F', ),
new EData('C', 'D', ),
new EData('C', 'E', ),
new EData('C', 'F', ),
new EData('D', 'E', ),
new EData('E', 'F', ),
new EData('E', 'G', ),
new EData('F', 'G', )
};
int vlen = sizeof(vexs)/sizeof(vexs[]);
int elen = sizeof(edges)/sizeof(edges[]);
listUDG* pG = new listUDG(vexs, vlen, edges, elen); pG->PrintUDG(); // 打印图
pG->Prim();
pG->Kruskal();
for (int i = ; i < elen; i ++)
{
delete edges[i];
}
return;
}

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

数据结构之最小生成树Kruskal算法的更多相关文章

  1. 数据结构:最小生成树--Kruskal算法

    Kruskal算法 Kruskal算法 求解最小生成树的还有一种常见算法是Kruskal算法.它比Prim算法更直观.从直观上看,Kruskal算法的做法是:每次都从剩余边中选取权值最小的,当然,这条 ...

  2. 【转】最小生成树——Kruskal算法

    [转]最小生成树--Kruskal算法 标签(空格分隔): 算法 本文是转载,原文在最小生成树-Prim算法和Kruskal算法,因为复试的时候只用到Kruskal算法即可,故这里不再涉及Prim算法 ...

  3. 模板——最小生成树kruskal算法+并查集数据结构

    并查集:找祖先并更新,注意路径压缩,不然会时间复杂度巨大导致出错/超时 合并:(我的祖先是的你的祖先的父亲) 找父亲:(初始化祖先是自己的,自己就是祖先) 查询:(我们是不是同一祖先) 路径压缩:(每 ...

  4. 并查集与最小生成树Kruskal算法

    一.什么是并查集 在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集的合并及查询问题.有一个联合-查找算法(union-find algorithm)定义了两个用于次数据结构的操作: Fi ...

  5. 最小生成树——Kruskal算法理解

    背景:本文是在小甲鱼数据结构教学视频中的代码的基础上,添加详细注释而完成的.该段代码并不完整,仅摘录了核心算法部分,结合自己的思考,谈谈理解. Prim算法理解: 如图(摘录自小甲鱼教学视频中的图片) ...

  6. 最小生成树——kruskal算法

    kruskal和prim都是解决最小生成树问题,都是选取最小边,但kruskal是通过对所有边按从小到大的顺序排过一次序之后,配合并查集实现的.我们取出一条边,判断如果它的始点和终点属于同一棵树,那么 ...

  7. 最小生成树Kruskal算法

    Kruskal算法就是把图中的所有边权值排序,然后从最小的边权值开始查找,连接图中的点,当该边的权值较小,但是连接在途中后会形成回路时就舍弃该边,寻找下一边,以此类推,假设有n个点,则只需要查找n-1 ...

  8. 最小生成树------Kruskal算法

    Kruskal最小生成树算法的概略描述:1 T=Φ:2 while(T的边少于n-1条) {3 从E中选取一条最小成本的边(v,w):4 从E中删去(v,w):5 if((v,w)在T中不生成环) { ...

  9. 求最小生成树——Kruskal算法

    给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法.这篇文章先介绍Kruskal算法. Kruskal算法的基本思想:先将所有边按权值从小到大排序,然后按顺 ...

随机推荐

  1. zabbix监控php-fpm的性能

    zabbix监控php-fpm主要是通过nginx配置php-fpm的状态输出页面,在正则取值 要nginx能输出php-fpm的状态必须要先修改php-fpm的配置,这个配置没有开启nginx 就没 ...

  2. Spring框架下Junit测试

    Spring框架下Junit测试 一.设置 1.1 目录 设置源码目录和测试目录,这样在设置产生测试方法时,会统一放到一个目录,如果没有设置测试目录,则不会产生测试代码. 1.2 增加配置文件 Res ...

  3. MySql判断汉字、日期、数字的具体函数

    几个平常用的mysql函数,MySql判断汉字.日期.数字的具体函数分享给大家,具体内容如下 1.判断字符串是否为汉字 返回值:1-汉字 0-非汉字 ? 1 2 3 4 5 6 7 8 9 10 11 ...

  4. eclipse——添加Tomcat7.0服务器

    首先要安装好Tomcat 然后在eclipse中添加Tomcat 步骤如下 详细可参考这篇博客https://blog.csdn.net/u014079773/article/details/5139 ...

  5. Windows 10 操作系统删除Administrator登录选项

    在win10操作系统锁屏界面登录选项中突然出现了两个用户选项,一个是自己微软账户登录的用户,另一个是Administrator登录选项,每次看到好别扭,怎么去掉呢? 一.以管理员身份运行命令提示符(c ...

  6. Pandas字符串和文本数据

    在本章中,我们将使用基本系列/索引来讨论字符串操作.在随后的章节中,将学习如何将这些字符串函数应用于数据帧(DataFrame). Pandas提供了一组字符串函数,可以方便地对字符串数据进行操作. ...

  7. scala学习手记31 - Trait

    不知道大家对java的接口是如何理解的.在我刚接触到接口这个概念的时候,我将接口理解为一系列规则的集合,认为接口是对类的行为的规范.现在想来,将接口理解为是对类的规范多少有些偏颇,更恰当些的观点应该是 ...

  8. JavaScript tips —— 谈谈数组乱序

    前言 先看一个段代码 function randArr (arr) { return arr.sort(() => { return (Math.random() - 0.5); }); } 目 ...

  9. Spark 宽窄依赖

    面试时被问到spark RDD的宽窄依赖,虽然问题很简单,但是答得很不好.还是应该整理一下描述,这样面试才能答得更好. 看到一篇很好的文章,转载过来了.感觉比<spark技术内幕>这本书讲 ...

  10. 复制文件夹的方法 .net

    void CopyDirectory(string SourcePath,string DestinationPath) { //创建所有目录 foreach(string dirPath inDir ...