图论(六)--最小生成树

基于算法导论图算法-最小生成树(第23章)

  • 题目描述
  • 问题分析
  • 源代码
  • 结果截图

题目描述

分别使用Kruskal算法和Prime算法求最小生成树(无向图)

问题分析

这里写图片描述

这里写图片描述

源代码

const int MAXN = 110;//最大点数
const int MAXM = 10000;//最大边数
struct Edge {
    int u, v, w;
};
//const int INF = 0x3f3f3f3f;//已经在Graph.h中定义
void init_G(Graph G);//将图的链表表示改为矩阵表示
int kruskal(int n);//返回最小生成树的权值,如果不联通返回-1,最小生成树的边集存入到数组vis_edge中
int Prim_matrix(int cost[][MAXN], int n);//图的矩阵形式prime
int Prim(Graph G);//图的链表形式的prime
void print_minTree();
#include<algorithm>

struct Edge graph_edge[MAXM];//存储边的信息,包括起点终点和权值
int Graph_edgeNum;//图的边数,用于排序
struct Edge minT_edge[MAXN];//记录最小生成树的边集
int minT_edgeNum;//最小生成树边数,加边前赋值0

//并查集使用
int father[MAXN];
int _rank[MAXN];

void Make_Set(int n) {//将每个点独立
    for (int i = 0; i < n; i++) {
        father[i] = i;
        _rank[i] = 0;
    }
}

int find(int n) {//找根

    if (n != father[n]) father[n] = find(father[n]); //路径压缩 
    return father[n];

}

void Union(int x, int y) {//联合
    x = find(x);
    y = find(y);
    if (_rank[x] > _rank[y]) father[y] = x;
    else if (_rank[y] > _rank[x]) father[x] = y;
    else {
        father[x] = y;
        _rank[y]++;
    }
}

void addedge(Edge* tempEdge ,int u, int v, int w , int edge_count)//添边
{
    tempEdge[edge_count].u = u;
    tempEdge[edge_count].v = v;
    tempEdge[edge_count].w = w;
}

bool cmp(Edge a, Edge b) {//排序,从小到大按照权值进行排序,用于kruskal
    return a.w < b.w;
}

int cost[MAXN][MAXN];//用于prime_matrix
int pre[MAXN];//用于prime_matrix


void init_G(Graph G) {//将图的链表表示改为矩阵表示
    //将图的链表表示改为矩阵表示(prime_matrix的初始化)
    PtrToNode ptr;
    for (int i = 0; i < G->vexnum; i++) {
        for (int j = 0; j < G->vexnum; j++) {
            cost[i][j] = INF;
        }
        for (ptr = G->vertices[i].adjto; ptr != NULL; ptr = ptr->next) {
            cost[i][ptr->adjvex] = ptr->weight;
        }
        cost[i][i] = 0;
    }

    //将无向图转变为无向边的集合,kruskal的初始化
    for (int i = 0; i < G->vexnum; i++) {
        for (int j = i + 1; j < G->vexnum; j++) {
            if (cost[i][j]!=INF) {
                addedge(graph_edge, i, j, cost[i][j], Graph_edgeNum++);
            }
        }
    }

}



int kruskal(int n) {
/////////////////////////init();//初始化图的边集(可以通过用户输入的方式)

    //n为图顶点数,graph_edge为图的边集,以下进行kruskal
    Make_Set(n);
    sort(graph_edge, graph_edge+Graph_edgeNum, cmp);
    int minT_weight = 0;//权值初始为0
    minT_edgeNum = 0;
    for (int i = 0; i < Graph_edgeNum; i++) {
        int u = graph_edge[i].u;
        int v = graph_edge[i].v;
        int w = graph_edge[i].w;
        int t1 = find(u);
        int t2 = find(v);
        if (t1 != t2) {
            minT_weight += w;
            addedge(minT_edge, u, v, w, minT_edgeNum++);
            Union(u, v);
        }
        if (minT_edgeNum == n - 1) break;//如果选择的边够了顶点数-1,则不用再判断其他边
    }
    if (minT_edgeNum < n - 1) return -1;//如果图不联通,则达不到n-1
    else return minT_weight;
}



bool vis[MAXN];//记录顶点是否已经选入最小生成树
int lowWeight[MAXN];//保存正在建立的最小生成树到各顶点的权
int Prim_matrix(int cost[][MAXN], int n)//点是0~n-1
{
    //////////////////////////////////init1();//初始化权矩阵,可以用户输入

    int minT_weight = 0;
    memset(vis, false, sizeof(vis));
    vis[0] = true;//选择第一个点为顶点0
    printf("矩阵表示图prime选择点的顺序为:0 ");

    //刚开始最小生成树仅包含起始顶点0,所以lowWeight[i]即从顶点0到其他顶点的权(除有边连接的顶点外全部为无穷大)
    for (int i = 1; i < n; i++) { lowWeight[i] = cost[0][i]; pre[i] = 0; }

    minT_edgeNum = 0;
    for (int i = 1; i<n; i++)
    {
        int minWeight = INF;
        int nextVertex = -1;

        //选择出下一个顶点比较lowWeight,选出最小值(不包括已选择顶点)
        for (int j = 0; j<n; j++)
            if (!vis[j] && minWeight>lowWeight[j])
            {
                minWeight = lowWeight[j];
                nextVertex = j;
            }
        if (minWeight == INF)return -1;//原图不连通

        minT_weight += minWeight;//最小生成树权值增加
        vis[nextVertex] = true;//下一个顶点已选择
        printf("%d ", nextVertex);
        addedge(minT_edge, pre[nextVertex], nextVertex, minWeight, minT_edgeNum++);//添加选择的边到最小生成树边集

        //更新lowWeight[i]和pre[i],只需要比较新加入的顶点相邻的点即可
        for (int j = 0; j<n; j++)
            if (!vis[j] && lowWeight[j]>cost[nextVertex][j]) {
                lowWeight[j] = cost[nextVertex][j];
                pre[j] = nextVertex;
            }

    }
    printf("\n");
    return minT_weight;
}


struct Qnode {
    int v;
    int key;
    Qnode(int _v = -1, int _key = -1) :v(_v), key(_key) {}
    bool operator<(const Qnode& x)const {
        return key > x.key;//这样优先队列建立小根堆
    }
};

int Prim(Graph G)//点是0~n-1,算法思想与最短路dijkstra一致唯一不同的是松弛操作
{
    int s = 0;//第一个选择的点
    int minT_weight = 0;//初始化权值为0

   //初始化key和color(对应于prime_matrix中的lowWeight和vis数组)
    for (int i = 0; i < G->vexnum; i++) {
        G->vertices[i].key = INF;
        G->vertices[i].color = 0;//白色,未选入最小生成树
    }
    G->vertices[s].key = 0;


    priority_queue<Qnode> Q;
    Q.push(Qnode(s, G->vertices[s].key));
    Qnode temp;
    PtrToNode ptr;

    printf("链表表示图prime选择点的顺序为:");
    while (!Q.empty()) {
        temp = Q.top();
        Q.pop();
        int u = temp.v;

        //下面两行是为了避免已经选入最小生成树的顶点再次进行计算
        if (G->vertices[u].color) continue;
        G->vertices[u].color = 1;//黑色
        minT_weight += G->vertices[u].key;
        printf("%d ",u);

        for (ptr = G->vertices[u].adjto; ptr!=NULL; ptr = ptr->next) {
            //relax松弛操作
            if (!G->vertices[ptr->adjvex].color&&G->vertices[ptr->adjvex].key > ptr->weight) {
                G->vertices[ptr->adjvex].key = ptr->weight;
                G->vertices[ptr->adjvex].pred = u;
                Q.push(Qnode(ptr->adjvex, G->vertices[ptr->adjvex].key));
            }

        }
    }

    //判断所有顶点是否全部选入最小生成树,如果没有证明不联通
    int flag = 0;
    for (int i = 0; i < G->vexnum; i++) {
        if (!G->vertices[i].color) {
            flag = 1;
            break;
        }
    }
    printf("\n");
    if (flag) return - 1;
    return minT_weight;
}


void print_minTree() {

    for (int i = 0; i < minT_edgeNum; i++) {
        printf("%d-%d 权值为:%d\n", minT_edge[i].u, minT_edge[i].v,minT_edge[i].w);
    }
}


int main() {

    //无向图的随机生成([图论(一)图的建立](http://blog.csdn.net/deep_kang/article/details/70877468))
    CreateRandomUndirectGraph();
    Graph G = CreateUndirectGraph();
    printf("打印图结构:\n");
    print_EdgeWeight(G);

    /*printf("\n下面是bfs:\n");
    BFS(G, 0);*/
    /*printf("\n下面是dfs:\n");
    DFS(G);*/

    /*printf("拓扑排序:\n");
    if (topSort_queue(G)) {
        print_order_queue(G);
        topSort_dfs(G);
        print_order_dfs(G);
    }
    else {
        printf("失败,此图不满足有向无环图。\n");
    }*/

    //printf("\n求强连通分量:\n");
    //strongly_connected_components(G);

    init_G(G);//将图的链表表示改为矩阵表示,并将无向边存入边集数组中,用于prime_matrix和kruskal
    int sumWeight = kruskal(G->vexnum);
    if (sumWeight != -1) {
        printf("kruskal最小生成树(边权和为%d):\n", sumWeight);
        print_minTree();
    }
    else {
        printf("此图不连通\n");
    }

    sumWeight = Prim(G);
    if (sumWeight != -1) {
        printf("Prim最小生成树(边权和为%d):\n", sumWeight);
        print_minTree();
    }
    else {
        printf("此图不连通\n");
    }

    extern int cost[MAXN][MAXN];
    sumWeight = Prim_matrix(cost, G->vexnum);
    if (sumWeight != -1) {
        printf("Prim_matrix最小生成树(边权和为%d):\n", sumWeight);
        print_minTree();
    }
    else {
        printf("此图不连通\n");
    }

    return 0;
}

结果截图

结果展示以9个顶点为例,三种方式结果一致。

这里写图片描述

猜你喜欢

转载自blog.csdn.net/deep_kang/article/details/71628022