[图] Prim算法实现-求连通图的最小生成树(用孩子兄弟CSTree)表示-邻接表(ALGraph)-C语言实现
普里姆算法(邻接表)
思想
从某个顶点开始(不要把它看成一个单独的顶点,把它看成只有一个结点的子生成树)
在第一步的生成树的相邻边中,选一条最小的边,将最小的边和边的另一个结点并入子生成树中(生成树就长大了一点)
继续,直到所有的顶点都被并入了生成树
步骤
1.取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w
2.在添加的顶点 w 和已经在生成树上的顶点v 之间必定存在一条边,
3.并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。
4.之后继续往生成树上添加顶点,直至生成树上含有 n-1 个顶点为止。
性能
O(n^2),适合稠密图(边多的图)
另一个【克鲁斯卡尔算法】O(eloge):适合稀疏图(边少的图)
举例
1.蓝色的顶点:还未考虑的顶点
2.黄色的顶点:已经考虑完的顶点
3.蓝色的线:未考虑的边
4.紫色的线:考虑范围内的边
5.红色的线:生成树的边(最短的边)
将a纳入考虑的顶点,变成黄色
第一步:考虑黄色顶点【a】
1.考虑与a相连的所有边(紫色的边),取出 最短的边:18
2.将最短边,纳入生成树的边(变红色)
3.将顶点b纳入已考虑的顶点(变黄色)
第二步:考虑黄色的顶点【a,b】
1.考虑与a,b相连的所有边(紫色的边),取出最短的边8
2.将最短边,纳入生成树的边(变红色)
3.将顶点c纳入已考虑的顶点(变黄色)
第三步:继续考虑黄色的顶点【a,b,c】
1.考虑与a,b,c相连的所有边(紫色的边),取出最短的边20
2.将最短边,纳入生成树的边(变红色)
3.将顶点d纳入已考虑的顶点(变黄色)
最终
实现
记:
1.黄色的顶点,为顶点集【U】,即已落在生成树上的顶点
2.蓝色的顶点,为顶点集【V-U】,即尚未落在生成树上的顶点
设置一个辅助数组
1.下标:顶点v
2.lowcost:记录【黄色的点们】到【v】的最短边
3.adjvex:记录【黄色的点集】中哪一个顶点,到v最短
邻接表生成
#define VertexType char //点类型
#define VRType int //边类型
#define maxSize 100
void Visit(VertexType e) {
printf("%c", e);
}
#define MAX_VERTEX_NUM 20
typedef enum { DG, UDG } GraphKind;
typedef struct ArcNode {
int adjV; //边指向的顶点
VRType weight; //权重
struct ArcNode* next;
}ArcNode; //边
typedef struct VNode {
VertexType data;
ArcNode* firstarc;
}VNode, AdjList[MAX_VERTEX_NUM]; //顶点
typedef struct {
GraphKind kind;
int vernum, arcnum;
AdjList vers;
}ALGraph;
Status InitGraph_AL(ALGraph* pG) { //初始化
int i;
pG->arcnum = 0;
pG->vernum = 0;
for (i = 0; i < MAX_VERTEX_NUM; ++i)
pG->vers[i].firstarc = NULL; //VC++6.0中指针初始化为0xcccccccc
return OK;
}
int LocateVex_AL(ALGraph G, VertexType e) { //定位值为e的元素下标
int i;
for (i = 0; i < G.vernum; ++i) {
if (G.vers[i].data == e) {
return i;
}
}
return -1;
}
// 创建无向图的邻接表--带权
Status CreateUDG_AL(ALGraph* pG) {
//输入规则:顶点数目->弧的数目->各顶点的信息->各条弧的信息
int i, a, b, w;
char h = {'\0'}, t = { '\0' };
ArcNode* p, * q;
InitGraph_AL(pG); //VC++6.0中指针初始化为0xcccccccc,如果不将指针初始化为NULL,会出错
//图的类型
pG->kind = DG;
//顶点数目
printf("顶点数目:");
scanf_s("%d", &i); if (i < 0) return ERROR;
pG->vernum = i;
//弧的数目
printf("\n弧的数目:");
scanf_s("%d", &i); if (i < 0) return ERROR;
getchar();
pG->arcnum = i;
//各顶点信息
for (i = 0; i < pG->vernum; ++i)
{
printf("第%d顶点信息:", i + 1);
scanf_s("%c",&h, 1);
getchar();
pG->vers[i].data = h;
//弧的信息
}
for (i = 0; i < pG->arcnum; ++i) {
printf("输入第%d边的信息(vi,vj)和权值:", i + 1);
scanf_s("%c %c %d", &h,1,&t,1, &w); //结点
getchar();
a = LocateVex_AL(*pG, h); //定位
b = LocateVex_AL(*pG, t); //定位
if (a < 0 || b < 0) return ERROR; //定位错误
// a-->b
p = (ArcNode*)malloc(sizeof(ArcNode)); if (!p) exit(OVERFLOW);
p->adjV = b;p->next = NULL;p->weight = w;
if (pG->vers[a].firstarc) { //已经有边了
//寻找尾节点 ----尾插
for (q = pG->vers[a].firstarc; q->next; q = q->next);//找到最后一条
q->next = p;
}
else { //第一条边
pG->vers[a].firstarc = p;
}
// b-->a
p = (ArcNode*)malloc(sizeof(ArcNode)); if (!p) exit(OVERFLOW);
p->adjV = a;p->next = NULL;p->weight = w;
if (pG->vers[b].firstarc) { //已经有边了
for (q = pG->vers[b].firstarc; q->next; q = q->next); //找到最后一条
q->next = p;
}
else { //第一条边
pG->vers[b].firstarc = p;
}
}
return OK;
}
求最小生成树-Prim
*----------------------------------------------------------------
|求最小生成树-Prim |
----------------------------------------------------------------*/
#define INF 10000
typedef struct CSNode {
VertexType data;
struct CSNode* firstchild;
struct CSNode* sibling;
VRType childW; //孩子的边权重
VRType siblingW; //兄弟的边权重
}CSNode, * CSTree;
//--------生成最小生成树——————————
VRType GetMSTByPrim(ALGraph G, int v0, CSTree* pT) {
int MST[MAX_VERTEX_NUM]; //当前的生成树:MST[v]=1 表示 结点v在当前的生成树上
VRType sum; //生成树的代价
VRType lowCost[MAX_VERTEX_NUM]; //当前的生成树 到 结点v 的最小边的权重是lowCost[v]
int lowNode[MAX_VERTEX_NUM]; //lowCost对应的顶点信息--> 到v的最小边是v->lowNode[v],权值为lowCost[v]
VRType min; //当前的最小边
int minNode; //最小边的另一个顶点
CSNode* tmp[MAX_VERTEX_NUM];
CSNode* csp;
ArcNode* p;
int i, j, parent;
//初始化
for (i = 0; i < G.vernum; i++) {
MST[i] = 0;
lowCost[i] = INF;
}
//处理v0结点
MST[v0] = 1; //将v0并入生成树
tmp[v0] = (CSNode*)malloc(sizeof(CSNode));
if (!tmp[v0])
{
exit(OVERFLOW);
}
//为生成树创建v0结点
tmp[v0]->data = G.vers[v0].data;
tmp[v0]->firstchild = tmp[v0]->sibling = NULL; //赋值
for (p = G.vers[v0].firstarc; p; p = p->next) {
//当前生成树到adjV结点的最小边是:v0到adjV的边
lowCost[p->adjV] = p->weight; //记下权重
lowNode[p->adjV] = v0; //记下结点
}
sum = 0;
for (i = 0; i < G.vernum - 1; i++) { //还有n-1个结点要处理
//选出最短的边
min = INF; //设定开始的最小值
for (j = 0; j < G.vernum; j++) {
// 如果结点j还没有被并入生成树
if (MST[j] == 0 && lowCost[j] < min) {
min = lowCost[j];
minNode = j; //保存该顶点
}
}
//处理minNode
MST[minNode] = 1; //并入生成树
sum += lowCost[minNode]; //生成树的权重
tmp[minNode] = (CSNode*)malloc(sizeof(CSNode)); if (!tmp[minNode]) exit(OVERFLOW); //为生成树创建minNode结点
tmp[minNode]->data = G.vers[minNode].data; tmp[minNode]->firstchild = tmp[minNode]->sibling = NULL; //赋值
//连接边
parent = lowNode[minNode]; //minNode的爸爸是lowNode[minNode],边的权重为lowCost[minNode]
if (tmp[parent]->firstchild) { //第一个孩子存在
//找到最后一个孩子 ------尾插
for (csp = tmp[parent]->firstchild; csp->sibling; csp = csp->sibling);
csp->sibling = tmp[minNode]; //csp的兄弟是tmp[minNode]
csp->siblingW = lowCost[minNode]; //边权重
}
else { //没有第一个孩子
tmp[parent]->firstchild = tmp[minNode]; //孩子为tmp[minNode]
tmp[parent]->childW = lowCost[minNode]; //边的权重为lowCost[minNode]
}
//更新minNode
for (j = 0; j < G.vernum; j++) {
for (p = G.vers[minNode].firstarc; p; p = p->next) {
if (MST[p->adjV] == 0 && p->weight < lowCost[p->adjV]) {
lowCost[p->adjV] = p->weight;
lowNode[p->adjV] = minNode;
}
}
}
}
*pT = tmp[0];
return sum;
}
该方法只只获得最短路径不生成最小生成树
//--------只是打印生成树结构--------------
VRType GetMSTByPrimNP(ALGraph G, int v0) {
int MST[MAX_VERTEX_NUM]; //当前的生成树:MST[v]=1 表示 结点v在当前的生成树上
VRType sum; //生成树的代价
VRType lowCost[MAX_VERTEX_NUM]; //当前的生成树 到 结点v 的最小边的权重是lowCost[v]
int lowNode[MAX_VERTEX_NUM] = {0}; //lowCost对应的顶点信息--> 到v的最小边是v->lowNode[v],权值为lowCost[v]
ArcNode* p;
VRType min; //当前的最小边
int minNode; //最小边的另一个顶点
int i, j;
//初始化
for (i = 0; i < G.vernum; i++) {
MST[i] = 0;
lowCost[i] = INF;
}
//处理v0结点
MST[v0] = 1; //将v0并入生成树
for (p = G.vers[v0].firstarc; p; p = p->next) {
//当前生成树到adjV结点的最小边是:v0到adjV的边
lowCost[p->adjV] = p->weight; //记下权重
lowNode[p->adjV] = v0; //记下结点
}
sum = 0;
for (i = 0; i < G.vernum - 1; i++) { //还有n-1个结点要处理
//选出最短的边
min = INF; //设定开始的最小值
for (j = 0; j < G.vernum; j++) {
// 如果结点j还没有被并入生成树
if (MST[j] == 0 && lowCost[j] < min) {
min = lowCost[j];
minNode = j; //保存该顶点
}
}
//处理minNode
MST[minNode] = 1; //并入生成树
sum += lowCost[minNode]; //生成树的权重
printf("%c ->%c :%d\n", G.vers[lowNode[minNode]].data, G.vers[minNode].data ,min);
//更新minNode
for (j = 0; j < G.vernum; j++) {
for (p = G.vers[minNode].firstarc; p; p = p->next) {
if (MST[p->adjV] == 0 && p->weight < lowCost[p->adjV]) {
lowCost[p->adjV] = p->weight;
lowNode[p->adjV] = minNode;
}
}
}
}
return sum;
}
完整代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#ifndef BASE
#define BASE
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int Bool;
#endif
#define VertexType char //点类型
#define VRType int //边类型
#define maxSize 100
void Visit(VertexType e) {
printf("%c", e);
}
#define MAX_VERTEX_NUM 20
typedef enum { DG, UDG } GraphKind;
typedef struct ArcNode {
int adjV; //边指向的顶点
VRType weight; //权重
struct ArcNode* next;
}ArcNode; //边
typedef struct VNode {
VertexType data;
ArcNode* firstarc;
}VNode, AdjList[MAX_VERTEX_NUM]; //顶点
typedef struct {
GraphKind kind;
int vernum, arcnum;
AdjList vers;
}ALGraph;
Status InitGraph_AL(ALGraph* pG) { //初始化
int i;
pG->arcnum = 0;
pG->vernum = 0;
for (i = 0; i < MAX_VERTEX_NUM; ++i)
pG->vers[i].firstarc = NULL; //VC++6.0中指针初始化为0xcccccccc
return OK;
}
int LocateVex_AL(ALGraph G, VertexType e) { //定位值为e的元素下标
int i;
for (i = 0; i < G.vernum; ++i) {
if (G.vers[i].data == e) {
return i;
}
}
return -1;
}
// 创建无向图的邻接表--带权
Status CreateUDG_AL(ALGraph* pG) {
//输入规则:顶点数目->弧的数目->各顶点的信息->各条弧的信息
int i, a, b, w;
char h = {'\0'}, t = { '\0' };
ArcNode* p, * q;
InitGraph_AL(pG); //VC++6.0中指针初始化为0xcccccccc,如果不将指针初始化为NULL,会出错
//图的类型
pG->kind = DG;
//顶点数目
printf("顶点数目:");
scanf_s("%d", &i); if (i < 0) return ERROR;
pG->vernum = i;
//弧的数目
printf("\n弧的数目:");
scanf_s("%d", &i); if (i < 0) return ERROR;
getchar();
pG->arcnum = i;
//各顶点信息
for (i = 0; i < pG->vernum; ++i)
{
printf("第%d顶点信息:", i + 1);
scanf_s("%c",&h, 1);
getchar();
pG->vers[i].data = h;
//弧的信息
}
for (i = 0; i < pG->arcnum; ++i) {
printf("输入第%d边的信息(vi,vj)和权值:", i + 1);
scanf_s("%c %c %d", &h,1,&t,1, &w); //结点
getchar();
a = LocateVex_AL(*pG, h); //定位
b = LocateVex_AL(*pG, t); //定位
if (a < 0 || b < 0) return ERROR; //定位错误
// a-->b
p = (ArcNode*)malloc(sizeof(ArcNode)); if (!p) exit(OVERFLOW);
p->adjV = b;p->next = NULL;p->weight = w;
if (pG->vers[a].firstarc) { //已经有边了
//寻找尾节点 ----尾插
for (q = pG->vers[a].firstarc; q->next; q = q->next);//找到最后一条
q->next = p;
}
else { //第一条边
pG->vers[a].firstarc = p;
}
// b-->a
p = (ArcNode*)malloc(sizeof(ArcNode)); if (!p) exit(OVERFLOW);
p->adjV = a;p->next = NULL;p->weight = w;
if (pG->vers[b].firstarc) { //已经有边了
for (q = pG->vers[b].firstarc; q->next; q = q->next); //找到最后一条
q->next = p;
}
else { //第一条边
pG->vers[b].firstarc = p;
}
}
return OK;
}
/*----------------------------------------------------------------
|求最小生成树-Prim |
----------------------------------------------------------------*/
#define INF 10000
typedef struct CSNode {
VertexType data;
struct CSNode* firstchild;
struct CSNode* sibling;
VRType childW; //孩子的边权重
VRType siblingW; //兄弟的边权重
}CSNode, * CSTree;
//--------生成最小生成树——————————
VRType GetMSTByPrim(ALGraph G, int v0, CSTree* pT) {
int MST[MAX_VERTEX_NUM]; //当前的生成树:MST[v]=1 表示 结点v在当前的生成树上
VRType sum; //生成树的代价
VRType lowCost[MAX_VERTEX_NUM]; //当前的生成树 到 结点v 的最小边的权重是lowCost[v]
int lowNode[MAX_VERTEX_NUM]; //lowCost对应的顶点信息--> 到v的最小边是v->lowNode[v],权值为lowCost[v]
VRType min; //当前的最小边
int minNode; //最小边的另一个顶点
CSNode* tmp[MAX_VERTEX_NUM];
CSNode* csp;
ArcNode* p;
int i, j, parent;
//初始化
for (i = 0; i < G.vernum; i++) {
MST[i] = 0;
lowCost[i] = INF;
}
//处理v0结点
MST[v0] = 1; //将v0并入生成树
tmp[v0] = (CSNode*)malloc(sizeof(CSNode));
if (!tmp[v0])
{
exit(OVERFLOW);
}
//为生成树创建v0结点
tmp[v0]->data = G.vers[v0].data;
tmp[v0]->firstchild = tmp[v0]->sibling = NULL; //赋值
for (p = G.vers[v0].firstarc; p; p = p->next) {
//当前生成树到adjV结点的最小边是:v0到adjV的边
lowCost[p->adjV] = p->weight; //记下权重
lowNode[p->adjV] = v0; //记下结点
}
sum = 0;
for (i = 0; i < G.vernum - 1; i++) { //还有n-1个结点要处理
//选出最短的边
min = INF; //设定开始的最小值
for (j = 0; j < G.vernum; j++) {
// 如果结点j还没有被并入生成树
if (MST[j] == 0 && lowCost[j] < min) {
min = lowCost[j];
minNode = j; //保存该顶点
}
}
//处理minNode
MST[minNode] = 1; //并入生成树
sum += lowCost[minNode]; //生成树的权重
tmp[minNode] = (CSNode*)malloc(sizeof(CSNode)); if (!tmp[minNode]) exit(OVERFLOW); //为生成树创建minNode结点
tmp[minNode]->data = G.vers[minNode].data; tmp[minNode]->firstchild = tmp[minNode]->sibling = NULL; //赋值
//连接边
parent = lowNode[minNode]; //minNode的爸爸是lowNode[minNode],边的权重为lowCost[minNode]
if (tmp[parent]->firstchild) { //第一个孩子存在
//找到最后一个孩子 ------尾插
for (csp = tmp[parent]->firstchild; csp->sibling; csp = csp->sibling);
csp->sibling = tmp[minNode]; //csp的兄弟是tmp[minNode]
csp->siblingW = lowCost[minNode]; //边权重
}
else { //没有第一个孩子
tmp[parent]->firstchild = tmp[minNode]; //孩子为tmp[minNode]
tmp[parent]->childW = lowCost[minNode]; //边的权重为lowCost[minNode]
}
//更新minNode
for (j = 0; j < G.vernum; j++) {
for (p = G.vers[minNode].firstarc; p; p = p->next) {
if (MST[p->adjV] == 0 && p->weight < lowCost[p->adjV]) {
lowCost[p->adjV] = p->weight;
lowNode[p->adjV] = minNode;
}
}
}
}
*pT = tmp[0];
return sum;
}
//--------只是打印生成树结构--------------
VRType GetMSTByPrimNP(ALGraph G, int v0) {
int MST[MAX_VERTEX_NUM]; //当前的生成树:MST[v]=1 表示 结点v在当前的生成树上
VRType sum; //生成树的代价
VRType lowCost[MAX_VERTEX_NUM]; //当前的生成树 到 结点v 的最小边的权重是lowCost[v]
int lowNode[MAX_VERTEX_NUM] = {0}; //lowCost对应的顶点信息--> 到v的最小边是v->lowNode[v],权值为lowCost[v]
ArcNode* p;
VRType min; //当前的最小边
int minNode; //最小边的另一个顶点
int i, j;
//初始化
for (i = 0; i < G.vernum; i++) {
MST[i] = 0;
lowCost[i] = INF;
}
//处理v0结点
MST[v0] = 1; //将v0并入生成树
for (p = G.vers[v0].firstarc; p; p = p->next) {
//当前生成树到adjV结点的最小边是:v0到adjV的边
lowCost[p->adjV] = p->weight; //记下权重
lowNode[p->adjV] = v0; //记下结点
}
sum = 0;
for (i = 0; i < G.vernum - 1; i++) { //还有n-1个结点要处理
//选出最短的边
min = INF; //设定开始的最小值
for (j = 0; j < G.vernum; j++) {
// 如果结点j还没有被并入生成树
if (MST[j] == 0 && lowCost[j] < min) {
min = lowCost[j];
minNode = j; //保存该顶点
}
}
//处理minNode
MST[minNode] = 1; //并入生成树
sum += lowCost[minNode]; //生成树的权重
printf("%c ->%c :%d\n", G.vers[lowNode[minNode]].data, G.vers[minNode].data ,min);
//更新minNode
for (j = 0; j < G.vernum; j++) {
for (p = G.vers[minNode].firstarc; p; p = p->next) {
if (MST[p->adjV] == 0 && p->weight < lowCost[p->adjV]) {
lowCost[p->adjV] = p->weight;
lowNode[p->adjV] = minNode;
}
}
}
}
return sum;
}
void TreeVisit(VertexType e) {
printf("%c", e);
}
void PrintAsTree(CSTree T, int i) {
int cnt;
if (T) {
//输出空格
for (cnt = 1; cnt < i; cnt++) printf(" ");
//输出字符
TreeVisit(T->data);
printf("\n");
PrintAsTree(T->firstchild, i + 1);
PrintAsTree(T->sibling, i);
}
}
int main() {
ALGraph G;
int sum;
CSTree T;
G.kind = UDG;
CreateUDG_AL(&G);
sum = GetMSTByPrim(G, 0, &T);
printf("最小生成的权重和为:%d\n", sum);
PrintAsTree(T, 1);
sum = GetMSTByPrimNP(G, 0);
printf("最小生成的权重和为:%d\n", sum);
return 0;
}