首先给出 一些 概念问题:
1.生成树: 一个n个顶点的 连通图 的 极小连通子图。 它含有n个顶点,但只有 n-1条边,不存在回路。
2.最小生成树:一个带权的 无向连通图,求出 各边权值相加 最小的 生成树,叫做最小生成树。
所以 求最小生成树 首先 要满足: 1. 首先 是 无向图 2. 必须是 连通图(任意两个顶点可达)3.带权
简单的说 就是 必须是 连通网。
求 最小生成树,严蔚敏的 数据结构 给出了 两种 方法:普里姆算法和 克鲁斯卡尔算法。
普里姆算法:
克鲁斯卡尔算法:
克鲁斯卡尔算法舍弃的边, 是因为造成了 回路。
由于普里姆 算法 里 需要 大量 查找 两个顶点之间的 权值,所以 用 图的 邻接矩阵 表示法,最为合适,时间复杂度为O(1)。其他表示 方法 都需要 遍历 链表,为O(len),len为链表的表长。
完整代码工程文件网盘地址:点击打开链接
下面 给出 普里姆算法的代码:
//普里姆最小生成树算法
struct LowCost//最小代价数组
{
char toVex;//顶点名称
int cost;//代价
};
int minLowCost(LowCost * array,int vexNum){
int min = INFINITY;
int k = -1;
for (int i = 0; i < vexNum; i++){
int cost = array[i].cost;
if (cost < min && cost != 0 ){
min = array[i].cost;
k = i;
}
}
return k;
}
void miniSpanTree_Prim(MGraph g,char vex){
LowCost lcArray[MAX_VERTEX_NUM];
int k = graphLocation(g,vex);
for (int i = 0; i < g.vexNum; i++){ //初始化lcArray
lcArray[i].toVex = vex;
lcArray[i].cost = g.arcs[k][i].adj;
}
lcArray[k].cost = 0;//将k 加入到 集合 u
printf("-----------最小生成树----------------\n");
for (int i = 1; i < g.vexNum; i++){//总共n-1 次..
k = minLowCost(lcArray,g.vexNum);
printf("%c - %c 权值为%d\n",lcArray[k].toVex,g.vexs[k],lcArray[k].cost);
lcArray[k].cost = 0;//将k 加入到集合u 中.
for (int i = 0; i < g.vexNum; i++){//获取 新的 最小代价
int min = lcArray[i].cost;
if (min != 0 && g.arcs[k][i].adj < min ){//集合u中的 不参与
lcArray[i].toVex = g.vexs[k];
lcArray[i].cost = g.arcs[k][i].adj;
}
}
}
}
从代码可以 看出 普里姆 算法 的时间 复杂度 为 O(n*n),n为 顶点数,适合 求 边稠密的图。
克鲁斯卡尔算法代码如下:
具体思路是: 将 图的所有边 从小到大顺序存入数组中,然后 按顺组 顺序 取 n-1 个边,(n是顶点树),舍弃 会造成 回路的边,舍弃的边 不算在 n-1个边里。
克鲁斯卡尔算法 适合 用 多重邻接表 来 实现,邻接矩阵 数组 初始化 是 o(n * n/2) ,n是顶点数,邻接表 不宜 求出 所有边,而 多重链接表 的初始化 为 O(e),e为边数。
下面代码 没用用多重邻接表的方式,用的是 邻接矩阵。
//克鲁斯卡尔最小生成树.
struct Edge//边集
{
int begin;
int end;
int cost;
};
//快速排序边集数组
void qSort(Edge * array,int left,int right){
if (left >= right){
return;
}
int i = left;
int j = right;
Edge base = array[i];
while (j != i){
while (j > i && array[j].cost >= base.cost) j--;
while (j > i && array[i].cost <= base.cost) i++;
if (j > i){
Edge temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
array[left] = array[i];
array[i] = base;
qSort(array,left,i-1);
qSort(array,i+1,right);
}
//将邻接矩阵转换成边集数组,并用快速排序从 小到大排序.
void covertToEdgeArray(MGraph g,Edge * edgeArray){
//根据 矩阵的上三角 部分 来初始化 边集数组
int count = 0;
for (int i = 0; i < g.vexNum; i++){
for (int j = i+1; j < g.vexNum; j++){
int cost = g.arcs[i][j].adj;
if (cost != INFINITY){
edgeArray[count].begin = i;
edgeArray[count].end = j;
edgeArray[count].cost = cost;
count++;
}
}
}
//快速排序 边集数组
qSort(edgeArray,0,g.arcNum-1);
}
int getParent(int k,int * parent){
while (parent[k] > 0 ){
k = parent[k];
}
return k;
}
void miniSpanTree_Cruskal(MGraph g){
printf("\n-----------克鲁斯卡尔最小生成树----------------\n");
Edge edgeArray[MAX_EDGE_NUM];//边集.
int parent[MAX_VERTEX_NUM] = {0};
covertToEdgeArray(g,edgeArray);//将邻接矩阵的边按照从小到大顺序存入数组.
int times = 0;
for (int i = 0; i < g.arcNum; i++){
Edge edge = edgeArray[i];
int p1 = getParent(edge.begin,parent);
int p2 = getParent(edge.end,parent);
if (p1 != p2){//父节点不同,不存在回路
//parent[edge.begin] = edge.end;
parent[p1] = p2;
printf("%c - %c 权值为%d\n",g.vexs[edge.begin],g.vexs[edge.end],edge.cost);
times++;
if (times >= g.vexNum-1){
return;
}
}
}
}
克鲁斯卡尔算法的时间复杂度为 O(eloge),e为图的边数,适合求 边稀疏的图。
运行截图:
上面 的 无向网,根据下面书 上的 这张图来的,只不过 将 顶点名称 (v1,v2...v6) 改成 (abcdef)
原文地址:http://blog.csdn.net/fuming0210sc/article/details/45010873