数据结构与算法之连通图的最小生成树

1,154 阅读4分钟

首先我们要知道什么是连通图的最小生成树,我们需要注意的是这个最小生成树不是树,它依然是图。

连通图的生成树概念

定义

一个连通图的生成树是一个极小的连通子图,它包含了图中所有的顶点,但只能构成图的n-1条边

生成树的条件

  • 连通的
  • 包含了所有的顶点
  • 图中边的数量是n-1条

生成树的识别

  • 不满足连通的条件


  • 不满足包含所有顶点
  • 不满足边数是n-1条
  • 满足条件的生成树


求解最小生成树的算法

假设有N个顶点,每个顶点连接的路径不一样,请设计一个算法快速找出能覆盖所有顶点的路径

最小生成树的概念:构成连通图生成树的最小代价的生成树为最小生成树

Prim算法求解最小生成树

  • 算法思路分析


  • 核心代码实现

    /**
     时间复杂度O(n^2),n为顶点的个数
     */
    void PrimSearch(MGraph Map){
        int min,i,j,k;
        int sum = 0;
        //定义保存相关顶点的数组,该数组的下标对应结束顶点,下标对应值是开始顶点,比如v0->v1则为:adjvex[1] = 0;
        int adjvex[MAXVEX];
        //定义保存相关顶点权值的数组,该数组的下标对应顶点,下标对应的值是该顶点的权值
        int lowcost[MAXVEX] = {INFINITYC};
        //我们默认从V0顶点开始加入生成树
        adjvex[0] = 0;
        //下标0表示顶点是V0,值0在这里表示已经加入到了生成树
        lowcost[0] = 0;
        //循环除v0顶点外的所有顶点
        for (i = 1; i<Map.numVertexes; i++) {
            //取出与v0顶点有关的权值
            lowcost[i] = Map.arc[0][i];
            //初始化相关顶点都起始位置都是v0顶点
            adjvex[i] = 0;
        }
        //循环除v0外的所有顶点
        for (i = 1; i<Map.numVertexes; i++) {
            //设置最小值为无穷
            min = INFINITYC;
            //每次循环j,k都重新设置
            j = 1;
            k = 0;
            //遍历剩余的顶点,查找最小权值的两个顶点
            while (j<Map.numVertexes) {
                //找到权值小于min并且没有加入到生成树的顶点
                if (lowcost[j] != 0 && lowcost[j] < min) {
                    //更新最小值
                    min = lowcost[j];
                    //记录最小值的顶点
                    k = j;
                }
                j++;
            }
            //打印路径以及权值,adjvex[k]是起点顶点,k是结束顶点
            printf("(v%d,v%d),权值为:%2d\n",adjvex[k],k,Map.arc[adjvex[k]][k]);
            sum += Map.arc[adjvex[k]][k];
            //设置k顶点已经加入了生成树
            lowcost[k] = 0;
            //循环遍历剩余顶点,更新adjvex数组以及lowcost数组
            for (j = 1; j<Map.numVertexes; j++) {
                //如果顶点j没有加入生成树并且与k顶点相关的权值小于min
                if (lowcost[j] != 0 && Map.arc[k][j] < lowcost[j]) {
                    lowcost[j] = Map.arc[k][j];
                    adjvex[j] = k;
                }
            }
        }
        printf("sum = %d\n",sum);
    }
    
    

Kruskal算法

  • 算法思路分析


  • 核心代码实现

    /* 对边集数组Edge结构的定义 */
    typedef struct {
        int begin;
        int end;
        int weight;
    }Edge ;
    #pragma mark - 给边数组排序
    void RankSort(Edge edges[], MGraph *Map){
        //对权值从小到大排序
        int tempValue;
        for (int i = 0; i<Map->numEdges; i++) {
            //j不能跟自己比所以要从i+1开始
            for (int j = i + 1; j<Map->numEdges; j++) {
                if (edges[i].weight > edges[j].weight) {
                    //交换begain
                    tempValue = edges[i].begin;
                    edges[i].begin = edges[j].begin;
                    edges[j].begin = tempValue;
                    //交换end
                    tempValue = edges[i].end;
                    edges[i].end = edges[j].end;
                    edges[j].end = tempValue;
                    //交换权值
                    tempValue = edges[i].weight;
                    edges[i].weight = edges[j].weight;
                    edges[j].weight = tempValue;
                }
            }
        }
        //打印下交换后的数据
        printf("交换后的数据为:\n");
        for (int i = 0; i<Map->numEdges; i++) {
            printf("(v%d,v%d)的权值为:%d\n",edges[i].begin,edges[i].end,edges[i].weight);
        }
    }
    #pragma mark - 查找父顶点
    int FindParent(int *parent, int result){
        while (parent[result]>0) {
            result = parent[result];
        }
        return result;
    }
    #pragma mark - Kruskal算法最小生成树
    void KruskalSearch(MGraph Map){
        //1、构建边数组
        int i,j,m,n;
        int k = 0;
        Edge edges[MAXVEX];
        for (i = 0; i<Map.numVertexes - 1; i++) {//最后一个顶点不需要了,否则第二层循环不会执行
            for (j = i + 1; j<Map.numVertexes; j++) {
                if (Map.arc[i][j] < INFINITYC) {
                    edges[k].begin = i;
                    edges[k].end = j;
                    edges[k].weight = Map.arc[i][j];
                    k++;
                }
            }
        }
        //对Map的边数组进行排序
        RankSort(edges, &Map);
        //定义parent数组,其中喜下标为起始顶点,下标的值为结束顶点,当下标值为0时表示没有下一个顶点了
        int parent[MAXVEX] = {0};
        int sum = 0;
        printf("最小生成树\n");
        for (i = 0; i<Map.numEdges; i++) {
            //获取begin、end信息
            //如果m == n则会有闭环
            n = FindParent(parent, edges[i].begin);
            m = FindParent(parent, edges[i].end);
            if (n != m) {//没有闭环
                //保存到parent数组中,此时改顶点已经在生成树中
                parent[n] = m;
                printf("(v%d,v%d),权值为:%2d\n",edges[i].begin,edges[i].end,edges[i].weight);
                sum += edges[i].weight;
            }
        }
        printf("sum = %d\n",sum);
    }