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

来源:互联网 发布:数据库字段唯一性约束 编辑:程序博客网 时间:2024/05/28 11:50

基于算法导论图算法-最小生成树(第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);//图的矩阵形式primeint Prim(Graph G);//图的链表形式的primevoid 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_matrixint pre[MAXN];//用于prime_matrixvoid 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个顶点为例,三种方式结果一致。

这里写图片描述

0 0
原创粉丝点击