问题及代码:
/*
copyright (t) 2016,烟台大学计算机学院
*All rights reserved.
*文件名称:1.cpp
*作者:常锐
*完成日期:2016年11月24日
*版本号:v1.0
*问题描述:运行Prim算法,观察结果并领会其原理及问题求解过程。
*输入描述:无
*程序输出:测试结果
*/
测试用图:
graph.h:
#include <stdio.h>
#define MAXV 100 //定义最大顶点数100
#define INF 9999 //处理“无穷大”
typedef int InfoType; //定义顶点与边的相关信息
typedef int Vertex;
typedef struct //定义顶点类型
{
int no; //顶点编号
InfoType info; //顶点其他信息
} VertexType;
typedef struct //定义图邻接矩阵类型
{
int edges[MAXV][MAXV]; //邻接矩阵边数组
int n; //顶点数
int e; //边数
VertexType vexs[MAXV]; //存放顶点信息
} MGraph;
typedef struct ANode //定义边节点类型
{
int adjvex; //该边终点编号
struct ANode *nextarc; //指向下一条边的指针
InfoType info; //该边相关信息
} ArcNode;
typedef struct VNode //定义邻接表头节点类型
{
Vertex data; //顶点信息
ArcNode *firstarc; //指向第一条边的指针
} VNode;
typedef VNode AdjList[MAXV]; //AdjList: 邻接表类型
typedef struct //定义图邻接表类型
{
AdjList adjlist; //邻接表
int n; //图中顶点数
int e; //图中边数
} ALGraph;
void ArrayToMat(int *Arr, int n, MGraph &g); //用普通数组构造图的邻接矩阵
void ArrayToList(int *Arr, int n, ALGraph *&G); //用普通数组构造图的邻接表
void MatToList(MGraph g,ALGraph *&G); //将邻接矩阵g转换成邻接表G
void ListToMat(ALGraph *G,MGraph &g); //将邻接表G转换成邻接矩阵g
void DispMat(MGraph g); //输出邻接矩阵g
void DispAdj(ALGraph *G); //输出邻接表G
graph.cpp:
#include <malloc.h>
#include "graph.h"
//几点说明:
//功能:由一个反映图中顶点邻接关系的二维数组,构造出用邻接矩阵存储的图
//参数:Arr - 数组名,由于形式参数为二维数组时必须给出每行的元素个数,在此将参数Arr声明为一维数组名(指向int的指针)
// n - 矩阵的阶数
// g - 要构造出来的邻接矩阵数据结构
void ArrayToMat(int *Arr, int n, MGraph &g) //用普通数组构造图的邻接矩阵
{
int i,j;
int edgenum=0; //边数初始化为0
g.n=n;
for(i=0;i<g.n;i++)
{
for(j=0;j<g.n;j++)
{
g.edges[i][j]=Arr[i*n+j]; //计算存储位置
if(g.edges[i][j]!=0 && g.edges[i][j]!=INF)
edgenum++;
}
}
g.e=edgenum;
}
void ArrayToList(int *Arr, int n, ALGraph *&G) //用普通数组构造图的邻接表
{
int i,j;
int edgenum=0; //边数初始化为0
ArcNode *p; //后续操作中创建的新节点
G=(ALGraph *)malloc(sizeof(ALGraph));
G->n=n;
for(i=0;i<n;i++) //邻接表所有头节点指针域置初值
G->adjlist[i].firstarc=NULL;
for(i=0;i<n;i++) //遍历邻接矩阵中的每个元素
{
for(j=n-1;j>=0;j--)
{
if(Arr[i*n+j]!=0)
{
p=(ArcNode *)malloc(sizeof(ArcNode)); //创建节点*p
p->adjvex=j;
p->info=Arr[i*n+j];
p->nextarc=G->adjlist[i].firstarc; //头插法插入*p
G->adjlist[i].firstarc=p; //指向第一条边的指针指向*p
}
}
}
G->e=edgenum;
}
void MatToList(MGraph g,ALGraph *&G) //将邻接矩阵g转换成邻接表G
{
int i,j;
ArcNode *p;
G=(ALGraph *)malloc(sizeof(ALGraph));
for(i=0;i<g.n;i++) //给邻接表所有头节点的指针域置初值
G->adjlist[i].firstarc=NULL;
for(i=0;i<g.n;i++) //遍历邻接矩阵中的每个元素
{
for(j=g.n-1;j>=0;j--)
{
if(g.edges[i][j]!=0)
{
p=(ArcNode *)malloc(sizeof(ArcNode)); //创建一个节点*p
p->adjvex=j; //终点编号赋值
p->nextarc=G->adjlist[i].firstarc; //头插法插入节点*p
G->adjlist[i].firstarc=p; //连接
}
}
}
G->n=g.n;
G->e=g.e;
}
void ListToMat(ALGraph *G,MGraph &g) //将邻接表G转换成邻接矩阵g
{
//前提要求:g的实参调用前已经初始化为全0
int i;
ArcNode *p;
for(i=0;i<G->n;i++)
{
p=G->adjlist[i].firstarc; //*p指向每个顶点的第一条边
while(p!=NULL) //依次遍历
{
g.edges[i][p->adjvex]=1; //p不为空指针时对应矩阵元素赋值1
p=p->nextarc; //*p指向下一条边
}
}
g.n=G->n;
g.e=G->e;
}
void DispMat(MGraph g) //输出邻接矩阵g
{
int i,j;
for(i=0;i<g.n;i++)
{
for(j=0;j<g.n;j++)
if(g.edges[i][j]==INF)
printf("%3s","∞");
else
printf("%3d",g.edges[i][j]);
printf("\n");
}
}
void DispAdj(ALGraph *G) //输出邻接表G
{
int i;
ArcNode *p;
for (i=0; i<G->n; i++)
{
p=G->adjlist[i].firstarc;
printf("%3d: ",i);
while (p!=NULL)
{
printf("-->%d/%d ",p->adjvex,p->info);
p=p->nextarc;
}
printf("\n");
}
}
main.cpp:
#include <stdio.h>
#include <malloc.h>
#include "graph.h"
void Prim(MGraph g,int v)
{
int lowcost[MAXV]; //顶点i是否在U中
int min;
int closest[MAXV],i,j,k;
for (i=0; i<g.n; i++) //给lowcost[]和closest[]置初值
{
lowcost[i]=g.edges[v][i];
closest[i]=v;
}
for (i=1; i<g.n; i++) //找出n-1个顶点
{
min=INF;
for (j=0; j<g.n; j++) //在(V-U)中找出离U最近的顶点k
if (lowcost[j]!=0 && lowcost[j]<min)
{
min=lowcost[j];
k=j; //k记录最近顶点的编号
}
printf(" 边(%d,%d)权为:%d\n",closest[k],k,min);
lowcost[k]=0; //标记k已经加入U
for (j=0; j<g.n; j++) //修改数组lowcost和closest
if (g.edges[k][j]!=0 && g.edges[k][j]<lowcost[j])
{
lowcost[j]=g.edges[k][j];
closest[j]=k;
}
}
}
int main()
{
MGraph g;
int A[6][6]=
{
{0,10,INF,INF,19,21},
{10,0,5,6,INF,11},
{INF,5,0,6,INF,INF},
{INF,6,6,0,18,14},
{19,INF,INF,18,0,33},
{21,11,INF,14,33,0}
};
ArrayToMat(A[0], 6, g);
printf("最小生成树构成:\n");
Prim(g,0);
return 0;
}
运行结果:
知识点总结:
Prim算法
心得体会:
Prim算法:逐个加入顶点,体现了“贪心策略”(即从选定的顶点开始,依次加入权值最小出边对应的顶点),注意不能形成回路。