数据结构之 图的最小生成树

一、实验目的
1、了解图的生成树和最小生成树的概念,掌握图的二种常用存贮结构。
2、掌握最小生成树的普里姆算法和克努斯卡尔算法,掌握这二种算法的存贮结构及特点。
二. 实验内容与要求
1.设计一个带树图,并用二种存贮结构存贮。
2. 根据普里姆算法及相应的存贮结构,构造一颗最小生成树。
3. 根据克努斯卡尔算法及相应的存贮结构,构造一颗最小生成树。
4. 实现提示:
   首先设计一个带树图,并将其用二种方式进行存贮;再按照二种算法的要求构造最小生成树。
   要求有相应的交互信息,并根据输出结果进行检验。




#include <stdio.h>

#include <malloc.h>
typedef int InfoType;
typedef int Vertex;
#define MAXV 100 //最大顶点个数
#define INF 32767 //INF表示∞
#define MAXE 100 //最多边数
//以下定义邻接矩阵类型
typedef struct 
{   int no; //顶点编号
InfoType info; //顶点其他信息
} VertexType; //顶点类型
typedef struct   //图的定义
{   int edges[MAXV][MAXV]; //邻接矩阵
    int n,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,e;                 //图中顶点数n和边数e
} ALGraph;                   //图的邻接表类型


void MatToList1(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 && g.edges[i][j]!=INF) //存在一条边
{   
  p=(ArcNode *)malloc(sizeof(ArcNode)); //创建一个节点*p
p->adjvex=j;
p->info=g.edges[i][j];
p->nextarc=G->adjlist[i].firstarc; //将*p链到链表后
G->adjlist[i].firstarc=p;
}
G->n=g.n;G->e=g.e;
}
void ListToMat1(ALGraph *G,MGraph &g)
//将邻接表G转换成邻接矩阵g
{
int i,j;
ArcNode *p;
for (i=0;i<G->n;i++)       //g.edges[i][j]赋初值0
  for (j=0;j<G->n;j++)
if (i==j)
g.edges[i][j]=0;
else
g.edges[i][j]=INF;
for (i=0;i<G->n;i++) 
{
p=G->adjlist[i].firstarc;
   while (p!=NULL) 
{
g.edges[i][p->adjvex]=p->info;
   p=p->nextarc;
}
}
g.n=G->n;g.e=G->e;
}
void DispMat1(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 DispAdj1(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("%3d(%d)",p->adjvex,p->info);
p=p->nextarc;
}
printf("\n");
}
}


void Prim(MGraph g,int v)
{
int lowcost[MAXV],min,n=g.n;
int closest[MAXV],i,j,k;
for (i=0;i<n;i++)           //给lowcost[]和closest[]置初值
{
lowcost[i]=g.edges[v][i];
closest[i]=v;
}
for (i=1;i<n;i++)           //找出n-1个顶点
{   
min=INF;
   for (j=0;j<n;j++)       //在(V-U)中找出离U最近的顶点k
  if (lowcost[j]!=0 && lowcost[j]<min) 
{
min=lowcost[j]; k=j;  
}
printf("  边(%d,%d)权为:%d\n",closest[k],k,min);
lowcost[k]=0;         //标记k已经加入U
for (j=0;j<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; 
}
}
}
typedef struct 
{ int u; //边的起始顶点
    int v; //边的终止顶点
    int w; //边的权值
} Edge;
void SortEdge(MGraph g,Edge E[]) //从邻接矩阵产生权值递增的边集
{
int i,j,k=0;
Edge temp;
for (i=0;i<g.n;i++)
for (j=0;j<g.n;j++)
if (g.edges[i][j]<INF)
{
E[k].u=i;
E[k].v=j;
E[k].w=g.edges[i][j];
k++;
}
for (i=1;i<k;i++) //按权值递增有序进行直接插入排序 
{
temp=E[i];
j=i-1; //从右向左在有序区E[0..i-1]中找E[i]的插入位置
while (j>=0 && temp.w<E[j].w) 
{
E[j+1]=E[j]; //将权值大于E[i].w的记录后移
j--;
}
E[j+1]=temp;      //在j+1处插入E[i]
}
}
void Kruskal(Edge E[],int n,int e)
{
int i,j,m1,m2,sn1,sn2,k;
int vset[MAXE];
for (i=0;i<n;i++) vset[i]=i; //初始化辅助数组
k=1;                 //k表示当前构造最小生成树的第几条边,初值为1
j=0;                 //E中边的下标,初值为0
while (k<n)       //生成的边数小于n时循环
{
m1=E[j].u;m2=E[j].v;        //取一条边的头尾顶点
sn1=vset[m1];sn2=vset[m2]; //分别得到两个顶点所属的集合编号
if (sn1!=sn2)     //两顶点属于不同的集合,该边是最小生成树的一条边
{
printf("  (%d,%d):%d\n",m1,m2,E[j].w);
k++;                    //生成边数增1
for (i=0;i<n;i++)       //两个集合统一编号
if (vset[i]==sn2)   //集合编号为sn2的改为sn1
   vset[i]=sn1;
}
j++;   //扫描下一条边
}
}
void Prim1()
{
int i,j,u=3;
MGraph g;
int A[MAXV][MAXV]={
{0,1,2,3,INF,INF,INF},
{1,0,INF,INF,2,3,INF},
{2,INF,0,INF,INF,INF,INF},
{3,INF,INF,0,INF,INF,INF},
{INF,2,INF,INF,0,INF,4},
{INF,3,INF,INF,INF,0,INF},
{INF,INF,INF,INF,4,INF,0}};

g.n=6;g.e=10;
for (i=0;i<g.n;i++) //建立图8.16的邻接矩阵
for (j=0;j<g.n;j++)
g.edges[i][j]=A[i][j];


printf("普里姆算法求解结果:\n");
Prim(g,0);
printf("\n");
}
void Kruskal1()
{
int i,j,u=3;
MGraph g;
Edge E[MAXE];
int A[MAXV][MAXV]={
{0,1,2,3,INF,INF,INF},
{1,0,INF,INF,2,3,INF},
{2,INF,0,INF,INF,INF,INF},
{3,INF,INF,0,INF,INF,INF},
{INF,2,INF,INF,0,INF,4},
{INF,3,INF,INF,INF,0,INF},
{INF,INF,INF,INF,4,INF,0}};
g.n=6;g.e=10;
for (i=0;i<g.n;i++) //建立图8.16的邻接矩阵
for (j=0;j<g.n;j++)
g.edges[i][j]=A[i][j];
SortEdge(g,E);
printf("克鲁斯卡尔算法求解结果:\n");
Kruskal(E,g.n,g.e);
printf("\n");
}
void output2()
{   int i,j,u=3;
MGraph g;
Edge E[MAXE];
int A[MAXV][MAXV]={
{0,1,2,3,INF,INF,INF},
{1,0,INF,INF,2,3,INF},
{2,INF,0,INF,INF,INF,INF},
{3,INF,INF,0,INF,INF,INF},
{INF,2,INF,INF,0,INF,4},
{INF,3,INF,INF,INF,0,INF},
{INF,INF,INF,INF,4,INF,0}};
g.n=6;g.e=10;
for (i=0;i<g.n;i++) //建立图8.16的邻接矩阵
for (j=0;j<g.n;j++)
g.edges[i][j]=A[i][j];
SortEdge(g,E);
printf("图G的邻接矩阵:\n");
    DispMat1(g);
printf("\n");
}
void output1()
{
ALGraph *G;
MGraph g;
int i,j,v=0;
g.n=7;g.e=6;
int A[MAXV][MAXV]={
{0,1,2,3,INF,INF,INF},
{1,0,INF,INF,2,3,INF},
{2,INF,0,INF,INF,INF,INF},
{3,INF,INF,0,INF,INF,INF},
{INF,2,INF,INF,0,INF,4},
{INF,3,INF,INF,INF,0,INF},
{INF,INF,INF,INF,4,INF,0}};
for (i=0;i<g.n;i++)
for (j=0;j<g.n;j++)
g.edges[i][j]=A[i][j];
MatToList1(g,G);
printf("图G的邻接表:\n");DispAdj1(G); //输出邻接表
printf("\n");
}
void main()
{   int j;
    
{
      printf("\n\n********************************************************");
      printf("\n\n********************************************************");
      printf("\n***                 图G的操作                        ***\n\n");
      printf("***      请选择:                                    ***\n\n");
      printf("***                 1:图G的邻接表                    ***\n\n");
      printf("***            2:图G的邻接矩阵                  ***\n\n");
      printf("***                 3:普利姆算法求解                 ***\n\n");
      printf("***                 4:克鲁斯卡尔算法求解             ***\n\n");
      printf("***                 0:退出                           ***");
      printf("\n\n********************************************************");
      printf("\n\n********************************************************");
}


 while(1)
 {
printf("\n选择进行的操作");
  do
  {
 scanf("%d",&j);
 if(j<0||j>4)
 printf("输入错误,请重新输入\n");
  }while(j<0||j>4);
   switch(j)
  { case 1:
            output1();
printf("\n");
break;
    case 2: 
            output2();
printf("\n");
   break;
    case 3: Kruskal1();
   printf("\n");
   break;
case 4: Prim1();
   printf("\n");
break;
case 0: break;
default:
printf("输入错误,请重新输入:");
       
break;
}
}

}



有几个函数里面都加上同一个数组,看着实在是多余。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值