Prim算法

来源:互联网 发布:东北师大网络教育2018 编辑:程序博客网 时间:2024/06/09 17:55

普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。
它是从点的方面考虑构建一颗MST,大致思想是:设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V,再从集合U-V中找到另一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;以此类推,现在的集合V={a,b},再从集合U-V中找到另一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V,直至所有顶点全部被加入V,此时就构建出了一颗MST。因为有N个顶点,所以该MST就有N-1条边,每一次向集合V中加入一个点,就意味着找到一条MST的边。

如下图:
这里写图片描述

过程示意图:

(1)这里写图片描述

(2)这里写图片描述

(3)这里写图片描述

(4)这里写图片描述

(5)这里写图片描述

(6)这里写图片描述

(7)这里写图片描述

(8)这里写图片描述

即,首先选择一个结点作为起始点,在起始点附近寻找一个没有被收录的、距离最小的点,记录dist等信息,之后将其收录到另一个图中,dist[] =0表示已经收录,更新原来的图的dist,一直循环知道遍历所有结点。
代码如下:

// Prim.cpp : 定义控制台应用程序的入口点。#include "stdafx.h"#include "stdio.h"#include "stdlib.h"#define INFINITY 65535#define MaxVertexNum 100typedef int Vertex;typedef int WeightType;typedef char DataType;Vertex path[MaxVertexNum];/*邻接矩阵图(结点)的定义*/typedef struct GNode *PtrToGNode;struct GNode {    int Nv;    int Ne;    WeightType G[MaxVertexNum][MaxVertexNum];    DataType Data[MaxVertexNum];};typedef PtrToGNode MGraph;/*边的定义*/typedef struct ENode *PtrToENode;struct ENode {    Vertex V1, V2;    WeightType Weight;};typedef PtrToENode Edge;/* 邻接点的定义 */typedef struct AdjVNode *PtrToAdjVNode;struct AdjVNode {    Vertex AdjV;        /* 邻接点下标 */    WeightType Weight;    PtrToAdjVNode Next;};/*定点表头结点的定义*/typedef struct VNode {    PtrToAdjVNode FirstEdge;    /*边表头指针*/    DataType Data;              /* 存顶点的数据 */}AdjList[MaxVertexNum];         /* AdjList是邻接表类型 */                                /*邻接表图定义*/typedef struct G_Node *PtrToG_Node;struct G_Node {    int Nv;    int Ne;    AdjList G;};typedef PtrToG_Node LGraph;/*初始化生成图*/MGraph CreateGraph(int Num_Vertex){    MGraph Graph;    Vertex V, W;    Graph = (MGraph)malloc(sizeof(struct GNode));    Graph->Nv = Num_Vertex;    Graph->Ne = 0;    for (V = 0; V < Graph->Nv; V++) {        for (W = 0; W < Graph->Nv; W++) {            Graph->G[V][W] = INFINITY;        }    }    return Graph;}/*将得到的边数据保存到图*/void InsertEdge(MGraph Graph, Edge E){    Graph->G[E->V1][E->V2] = E->Weight;    Graph->G[E->V2][E->V1] = E->Weight;}/*生成完整图*/MGraph BuildGraph(){    MGraph Graph;    Edge E;    int Nv, i;    //Vertex V;    printf("Input the number of Vertex\n");    scanf_s("%d", &Nv);    Graph = CreateGraph(Nv);    printf("Input the number of Edge\n");    scanf_s("%d", &Graph->Ne);    if (Graph->Ne != 0) {        E = (Edge)malloc(sizeof(struct ENode));        for (i = 0; i < Graph->Ne; i++) {            printf("input E->V1,E->V2,E->Weight\n");            scanf_s("%d %d %d", &E->V1, &E->V2, &E->Weight);            InsertEdge(Graph, E);        }    }    for (Vertex i = 0; i < Graph->Nv; i++) {        for (Vertex j = 0; j < Graph->Nv; j++) {            if (Graph->G[i][j] < INFINITY) {                printf("Graph->G[%d][%d] = %d\n", i, j,Graph->G[i][j]);            }        }    }    /*printf("input the DataType\n");    for (V = 0; V<Graph->Nv; V++) {    scanf_s(" %c", &Graph->Data[V]);    }*/    return Graph;}Vertex FindMinDist(MGraph Graph, WeightType dist[]){    Vertex MinV, V;    WeightType MinDist = INFINITY;    for (V = 0; V < Graph->Nv; V++) {        if (dist[V] != 0 && dist[V] < MinDist) {            MinDist = dist[V];            MinV = V;        }    }    if (MinDist < INFINITY) {        return MinV;    }    else        return -1;}int Prim(MGraph Graph, MGraph &MST)/*这里传引用,为了确保传进来的MST里面的东西同步更新,最好传引用*/{    WeightType dist[MaxVertexNum], TotalWeight;    Vertex parent[MaxVertexNum], V, W;    Vertex i_position = 0;    int VCount;    Edge E;    /* 初始化,默认初始点下标是0 */    for (V = 0; V < Graph->Nv; V++) {        /* 这里假设若V到W没有直接的边,则Graph->G[V][W]定义为INFINITY */        dist[V] = Graph->G[0][V];        parent[V] = 0;        /* 暂且定义所有顶点的父结点都是初始点0 */    }    TotalWeight = 0;    VCount = 0;    //MST = CreateGraph(Graph->Nv);      /*如果这里再定义一次,相当于将指针指向别处,    类似于传进函数的一级指针指向别的地方,MST在函数内修改,而出了函数体,    相当于没有对MST做修改,如果传引用则无需考虑这些,因为他们是捆绑在一起的*/    E = (Edge)malloc(sizeof(struct ENode));    dist[0] = 0;    parent[0] = -1;    VCount++;    path[i_position] = 0;    i_position++;    for (Vertex i = 1; i < Graph->Nv; i++) {        /*Vertex从1开始,因为最开始的结点(0结点)已经被收录,故只需再收录Nv-1个结点*/        V = FindMinDist(Graph, dist);        if (V == -1)            break;        E->V1 = parent[V];         E->V2 = V;        E->Weight = dist[V];        InsertEdge(MST, E);        for (int m = 0; m < MST->Nv; m++) {            for (int n = 0; n < MST->Nv; n++) {                printf("MST->G[%d][%d] = %d\n", m, n, MST->G[m][n]);                /*if (MST->G[W][V] < INFINITY) {                printf("MST->G[%d][%d] = %d\n", V, W, MST->G[V][W]);                }*/            }        }        TotalWeight += dist[V];        dist[V] = 0;        VCount++;        path[i_position] = V;        i_position++;        for (W = 0; W < Graph->Nv; W++) {            if (dist[W] != 0 && Graph->G[V][W] < INFINITY) {                /* 若W是V的邻接点并且未被收录 */                if (Graph->G[V][W] < dist[W]) {                    /* 若收录V使得dist[W]变小 */                    dist[W] = Graph->G[V][W]; /* 更新dist[W] */                    parent[W] = V;  /* 更新树 */                }            }        }    }    if (VCount < Graph->Nv) {        TotalWeight = -1;    }    return TotalWeight;}void PrintPath(int Nv){    for (Vertex V = 0; V < Nv; V++)        printf("%d ", path[V]);}int main(){    Vertex V,W;    MGraph Graph = BuildGraph();    MGraph MST = CreateGraph(Graph->Nv);    int Weight = Prim(Graph, MST);    /*上面函数调用传引用是为了保证传进去的MST能够同步跟新*/    printf("%d\n", Weight);    PrintPath(MST->Nv);    printf("\n");    printf("Vertex's number of Graph MST %d\n", MST->Nv);    for (V = 0; V < MST->Nv; V++) {        for (W = 0; W < MST->Nv; W++) {            printf("MST->G[%d][%d] = %d\n", V, W, MST->G[V][W]);            /*if (MST->G[W][V] < INFINITY) {                printf("MST->G[%d][%d] = %d\n", V, W, MST->G[V][W]);            }*/        }    }    return 0;}