-
连通图的生成树概念
连通图的⽣成树定义:所谓⼀个连通图的⽣成树是⼀个极⼩的连通⼦图,它含有图中全部的n个顶点,但只⾜以 构成⼀颗树的n-1条边.例如下图(再多一条边就不是连通图的生成树)
连通图生成树的三个条件:- 图是连通图
- 图中包含N个顶点
- 图的边数等于N-1
-
最小生成树概念
把构成连通⽹的最⼩代价的⽣成树称为最⼩⽣成树
其实就是不但要达到连通图的生成图的三个条件,并且所有变得权值加起来还要是最小的
-
最⼩⽣成树Prim 算法解析与实现
- 算法思路以及步骤
大概的思路就是循环一个一个找,先找到v0到所有结点的前置记录下来,如果不能直接到达的权值设置为无穷大,这时候可以将0这个位置的下标j记为0 表示0位置这个顶点已经加入到最小生成树中了,然后遍历lowcost数组找到权值最小的顶点索引,再将该顶点对应到每个顶点的权值(已经加入到最小生成树的顶点除外)和lowcost数组中的前置进行对比如果更小更新lowcost数组的值然后再更新adjvex数组的值(如果是连通图的话冲一个顶点出发循环(顶点个数)次数,刚好吧图整个都遍历了一遍)
步骤:
1.创建两个数组lowcost和adjvex数组,lowcost用于存储权值,adjvex用来存储当前顶点的前驱顶点索引
2.遍历所有顶点,并将对应的权值和索引写入到对应的数组
3.最后的到的adjvex索引数组就是得出的最小生成树
- 代码实现
#include <stdio.h> #include "stdlib.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXVEX 100 /* 最大顶点数,应由用户定义 */ #define INFINITYC 65535 typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ typedef char ElemType; //typedef struct { // int weight; //权值 // ElemType data; //内容 //} vertex, *Vertexs; typedef struct { int numVertexes; //顶点的数量 int numEdges; //边数 int arc[MAXVEX][MAXVEX]; //编标信息 }Graph; void createPGraph(Graph * G){ int i, j; /* printf("请输入边数和顶点数:"); */ G->numEdges=15; G->numVertexes=9; for (i = 0; i < G->numVertexes; i++)/* 初始化图 */ { for ( j = 0; j < G->numVertexes; j++) { if (i==j) G->arc[i][j]=0; else G->arc[i][j] = G->arc[j][i] = INFINITYC; } } G->arc[0][1]=10; G->arc[0][5]=11; G->arc[1][2]=18; G->arc[1][8]=12; G->arc[1][6]=16; G->arc[2][8]=8; G->arc[2][3]=22; G->arc[3][8]=21; G->arc[3][6]=24; G->arc[3][7]=16; G->arc[3][4]=20; G->arc[4][7]=7; G->arc[4][5]=26; G->arc[5][6]=17; G->arc[6][7]=19; for(i = 0; i < G->numVertexes; i++) { for(j = i; j < G->numVertexes; j++) { G->arc[j][i] =G->arc[i][j]; } } for(i = 0; i < G->numVertexes; i++) { for(j = 0; j < G->numVertexes; j++) { printf("%d ",G->arc[i][j]); } printf("\n"); } } /** Prim实现思路 大概的思路就是循环一个一个找,先找到v0到所有结点的前置记录下来,如果不能直接到达的权值设置为无穷大,这时候可以将0这个位置的下标j记为0 表示0位置这个顶点已经加入到最小生成树中了,然后遍历lowcost数组找到权值最小的顶点索引,再将该顶点对应到每个顶点的权值(已经加入到最小生成树的顶点除外)和lowcost数组中的前置进行对比如果更小更新lowcost数组的值然后再更新adjvex数组的值(如果是连通图的话冲一个顶点出发循环(顶点个数)次数,刚好吧图整个都遍历了一遍) 步骤: 1.创建两个数组lowcost和adjvex数组,lowcost用于存储权值,adjvex用来存储当前顶点的前驱顶点索引 2.遍历所有顶点,并将对应的权值和索引写入到对应的数组 3.最后的到的adjvex索引数组就是得出的最小生成树 */ void prim(Graph G,int *adjvex){ //用于记录下一次比较用哪个顶点对应w的关系表比较 int k = 0; int min; //用于记录最小权值 //1.初始化索引数组(默认所有顶点对应的前驱顶点都是0) adjvex = (int *)calloc(G.numVertexes, sizeof(int)); //2.初始化权值数组 int lowcost[G.numVertexes]; for(int i = 0; i < G.numVertexes; i ++){ //应为默认所有的顶点的前驱顶点都是0,所以将0到所有顶点的权值写入到权值表中, //无法达到的顶点写入无穷大 lowcost[i] = G.arc[0][i]; } //3.开始遍历数组(直接冲1开始循环,应为0位置的顶点对应的信息已经写入到数组中) //达到更新lowcost和adjvex数组的几个条件 //1.新权值小于旧 的权值 int sum = 0; for(int i = 1; i < G.numVertexes; i ++){ min = INFINITYC; for(int n = 0; n < G.numVertexes; n ++){ if(lowcost[n] == 0){ continue; } if(lowcost[n] < min){ min = lowcost[n]; k = n; } } //将索引为k的顶点加入到最小生成树中 lowcost[k] = 0; printf("v%d -> v%d \n",adjvex[k],k); sum += min; for(int j = 0; j < G.numVertexes; j ++){ if(G.arc[k][j] < lowcost[j] && G.arc[k][j] > 0 && lowcost[j] > 0){ //满足条件开始更新lowcost和adjvex数组 lowcost[j] = G.arc[k][j]; adjvex[j] = i; } } } printf("%d",sum); } int main(int argc, const char * argv[]) { Graph G; createPGraph(&G); int *adjvex = NULL; prim(G,adjvex); return 0; } - 算法思路以及步骤
-
最⼩⽣成树Kruskal 算法解析与实现
-
算法思路
思路:首先要将邻接矩阵转换成边表数组,然后将边表数组的权值冲小到大排列, 然后再创建一个parent数组用来存放结点的父节点,然后遍历所有的边,通过parent 数组找到对应的对应边的链接信息避免闭环问题.如果不存在闭环则添加到最小生 成树中(也就是将对应的值存储在parent数组中)这个算法的精髓就是按照边表顺序从小到大一个边一个边的组合。只要组合成了一个生成树 那这时候的生成树就是最小生成树
-
步骤图解
-
代码实现
/** Kruskal算法求出最小生成树 思路:首先要将邻接矩阵转换成边表数组,然后将边表数组的权值冲小到大排列, 然后再创建一个parent数组用来存放结点的父节点,然后遍历所有的边,通过parent 数组找到对应的对应边的链接信息避免闭环问题.如果不存在闭环则添加到最小生 成树中(也就是将对应的值存储在parent数组中) 这个算法的精髓就是按照边表顺序从小到大一个边一个边的组合。只要组合成了一个生成树 那这时候的生成树就是最小生成树 */ typedef struct { int beginIndex; //起始顶点的索引 int endIndex; //终止顶点的索引 int weight; //权值 } Egde; //冒泡排序 void sort(Egde * Egdes,int num){ for(int i = 0; i < num; i ++){ for(int j = i + 1; j < num; j ++){ if(Egdes[i].weight > Egdes[j].weight){ Egde temp = Egdes[i]; Egdes[i] = Egdes[j]; Egdes[j] = temp; } } } printf("sdf"); } void kruskal(Graph G){ Egde Egdes[G.numEdges]; //现将所有的边信息录入到边表数组中 int k = 0; for(int i = 0; i < G.numVertexes; i ++){ for(int j = i; j < G.numVertexes; j ++){ if(G.arc[i][j] > 0 && G.arc[i][j] < INFINITYC){ Egdes[k].beginIndex = i; Egdes[k].endIndex = j; Egdes[k].weight = G.arc[i][j]; k++; } } } //然后将边表数组中的信息排序(从小到达排序) sort(Egdes, G.numEdges); //创建parent数组用于存放节点的父顶点 int parent[G.numVertexes]; //默认所有顶点对应的父顶点都是0 for(int i = 0; i < G.numVertexes; i ++){ parent[i] = 0; } //开始遍历 int sum = 0; for(int i = 0; i < G.numEdges; i ++){ Egde e = Egdes[i]; int n = e.beginIndex; int n_temp = parent[e.beginIndex]; while(n_temp != 0){ n = n_temp; n_temp = parent[n_temp]; } int m = e.endIndex; int m_temp = parent[e.endIndex]; while(m_temp != 0){ m = m_temp; m_temp = parent[m_temp]; } if(n != m){ parent[n] = m; /*打印最小生成树路径*/ printf("(%d, %d) %d\n", Egdes[i].beginIndex, Egdes[i].endIndex, Egdes[i].weight); sum += Egdes[i].weight; } } printf("%d",sum); } -