图的最小生成树算法

  • Post author:
  • Post category:其他




第1关:求图(邻接矩阵存储)最小生成树的普里姆(Prim)算法



任务描述

本关任务:图的存储结构为邻接矩阵,要求编写函数利用普里姆(Prim)算法求图的最小生成树。



测试说明

平台会对你编写的代码进行测试:

测试输入:

3

lt3.txt

0

输入说明:

第一行输入3,表示输入图的类型为无向网。

第二行输入文件名,该文件里保存了图的数据信息,内容如下:

5

8

0

1

2

3

4

0 1 1

0 2 3

0 3 4

0 4 7

1 2 2

2 3 5

2 4 8

3 4 6

第1行为图的顶点的个数n;

第2行为图的边的条数m;

第3行至第n+2行是n个顶点的数据;

第n+3行至第n+m+2行是m条边的数据;

第三行输入利用普里姆算法构造最小生成树的起点。

预期输出:

无向网

5个顶点8条边。顶点依次是: 0 1 2 3 4

图的邻接矩阵:

∞ 1 3 4 7

1 ∞ 2 ∞ ∞

3 2 ∞ 5 8

4 ∞ 5 ∞ 6

7 ∞ 8 6 ∞

用普里姆算法从g的第0个顶点出发输出最小生成树的各条边:

最小代价生成树的各条边为:

边(0,1),权值为1

边(1,2),权值为2

边(0,3),权值为4

边(3,4),权值为6

输出说明:

第一行输出图的类型。

第二行起输出图的顶点和边的数据信息。

最后分行输出从第0个顶点出发用普里姆算法构造的最小生成树的各条边。



代码如下

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h>
#include<limits.h> 

#include"MGraph.h"

typedef struct min
 { /* 记录从顶点集U到V-U的代价最小的边的辅助数组定义 */
   VertexType adjvex;
   VRType lowcost;
 }minside[MAX_VERTEX_NUM];


int minimum(minside SZ,MGraph G); // 求SZ.lowcost的最小正值,并返回其在SZ中的序号 
void MiniSpanTree_PRIM(MGraph G,VertexType u); // 用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边 	

int main()
{
	MGraph g;
  int n;
	CreateGraphF (g);  // 利用数据文件创建无向图
	Display(g);      // 输出无向图 
  scanf("%d",&n); 
	printf("用普里姆算法从g的第%d个顶点出发输出最小生成树的各条边:\n",n); 
	MiniSpanTree_PRIM(g,g.vexs[n]);  // Prim算法从第1个顶点构造最小生成树 
	return 0;
}

int minimum(minside SZ,MGraph G)
{ //求SZ.lowcost的最小正值,并返回其在SZ中的序号
  int i=0,j,k,min;
  while(!SZ[i].lowcost)
  	i++;
  min=SZ[i].lowcost;                         // 第一个不为0的值 
  k=i;
  for(j=i+1;j<G.vexnum;j++)
    if(SZ[j].lowcost>0&&min>SZ[j].lowcost) // 找到新的大于0的最小值 
    {
      min=SZ[j].lowcost;
      k=j;
    }
  return k;
}


void MiniSpanTree_PRIM(MGraph G,VertexType u)
 { 
	// 用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边  
	/********** Begin **********/    
   int i,j,k;
   minside closedge;
   k=LocateVex(G,u);
   for(j=0;j<G.vexnum;++j){
     strcpy(closedge[j].adjvex,u);
     closedge[j].lowcost=G.arcs[k][j].adj;
   }
   closedge[k].lowcost=0;
   printf("最小代价生成树的各条边为:\n");
   for(i=1;i<G.vexnum;++i){
     k=minimum(closedge,G);
     printf("边(%s,%s),权值为%d\n",closedge[k].adjvex,G.vexs[k],closedge[k].lowcost);
     closedge[k].lowcost=0;
     for(j=0;j<G.vexnum;++j)
      if(G.arcs[k][j].adj<closedge[j].lowcost){
        strcpy(closedge[j].adjvex,G.vexs[k]);
        closedge[j].lowcost=G.arcs[k][j].adj;
      }
   }
	/********** End **********/

 }



第2关:求图(邻接表存储)最小生成树的普里姆(Prim)算法



任务描述

本关任务:图的存储结构为邻接表,要求编写函数利用普里姆(Prim)算法求图的最小生成树。



测试说明

平台会对你编写的代码进行测试:

测试输入:

3

lt3.txt

0

输入说明:

第一行输入3,表示输入图的类型为无向网。

第二行输入文件名,该文件里保存了图的数据信息,内容如下:

5

8

0

1

2

3

4

0 1 1

0 2 3

0 3 4

0 4 7

1 2 2

2 3 5

2 4 8

3 4 6

第1行为图的顶点的个数n;

第2行为图的边的条数m;

第3行至第n+2行是n个顶点的数据;

第n+3行至第n+m+2行是m条边的数据;

第三行输入利用普里姆算法构造最小生成树的起点。

预期输出:

无向网

5个顶点:

0 1 2 3 4

8条弧(边):

0→4 :7 0→3 :4 0→2 :3 0→1 :1

1→2 :2 1→0 :1

2→4 :8 2→3 :5 2→1 :2 2→0 :3

3→4 :6 3→2 :5 3→0 :4

4→3 :6 4→2 :8 4→0 :7

用普里姆算法从g的第0个顶点出发输出最小生成树的各条边:

最小代价生成树的各条边为:

边(0,1),权值为1

边(1,2),权值为2

边(0,3),权值为4

边(3,4),权值为6

输出说明:

第一行输出图的类型。

第二行起输出图的顶点和边的数据信息。

最后分行输出从第1个顶点出发用普里姆算法构造的最小生成树的各条边。



代码如下

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h>
#include<limits.h> 
#include"ALGraph.h"
#define INFINITY 4270000 
typedef int VRType;    
typedef struct min
 { /* 记录从顶点集U到V-U的代价最小的边的辅助数组定义 */
   VertexType adjvex;
   VRType lowcost;
 }minside[MAX_VERTEX_NUM];
 
int GetWeight(ALGraph G,VertexType a,VertexType b );//获取权值
int minimum(minside SZ,ALGraph G);  // 求SZ.lowcost的最小正值,并返回其在SZ中的序号
void MiniSpanTree_PRIM(ALGraph G,VertexType u);// 用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边

int main()
{
    ALGraph g;
    int n;
    CreateGraphF (g);  // 利用数据文件创建无向图
    Display(g);      // 输出无向图  
    scanf("%d",&n);
    printf("用普里姆算法从g的第%d个顶点出发输出最小生成树的各条边:\n",n); 
    MiniSpanTree_PRIM(g,g.vertices [n].data);  // Prim算法从第1个顶点构造最小生成树 
}

int GetWeight(ALGraph G,VertexType a,VertexType b )//获取权值 
{    int pa,pb;
    pa=LocateVex(G,a);
    pb=LocateVex(G,b);
    ArcNode* p;
    p=G.vertices[pa].firstarc;
    if(pa == pb)
        return 0;
    while(p!=NULL)
    {    if( p->data.adjvex  == pb)
        return p->data.info;
        else
        p=p->nextarc;
    }
    return INFINITY;
}

int minimum(minside SZ,ALGraph G)
 { /* 求SZ.lowcost的最小正值,并返回其在SZ中的序号 */
   int i=0,j,k,min;
   while(!SZ[i].lowcost)
     i++;
   min=SZ[i].lowcost;                         /* 第一个不为0的值 */
   k=i;
   for(j=i+1;j<G.vexnum;j++)
     if(SZ[j].lowcost>0 && min>SZ[j].lowcost) /* 找到新的大于0的最小值 */
     {
       min=SZ[j].lowcost;
       k=j;
     }
   return k;
 }

void MiniSpanTree_PRIM(ALGraph G,VertexType u)
{ 
  // 用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边  
  /********** Begin **********/    
  int k;  
  minside sz;  
  k=LocateVex(G,u);  
  for(int i=0;i<G.vexnum;i++)  
  {  
    strcpy(sz[i].adjvex,u);  
    sz[i].lowcost=GetWeight(G,G.vertices[k].data ,G.vertices[i].data);  
  }  
  sz[k].lowcost=0; 
  printf("最小代价生成树的各条边为:\n");  
  for(int i=1;i<G.vexnum;i++)  
  {  
    k=minimum(sz,G);   
    printf("边(%s,%s),权值为%d\n", sz[k].adjvex,G.vertices [k].data,sz[k].lowcost); 
    sz[k].lowcost=0;      
    for(int j=0;j<G.vexnum;j++)  
    {  
      int min=GetWeight(G,G.vertices [k].data ,G.vertices[j].data);  
      if( min!=INFINITY && min!=0 && min<sz[j].lowcost)  
      { 
        strcpy(sz[j].adjvex,G.vertices[k].data );  
        sz[j].lowcost=GetWeight(G,G.vertices[k].data ,G.vertices[j].data);  
      }  
    }  
  }
  /********** End **********/
}



第3关:求图(邻接矩阵存储)最小生成树的克鲁斯卡尔(Kruskal)算法



任务描述

本关任务:图的存储结构为邻接矩阵,要求编写函数利用克鲁斯卡尔(Kruskal)算法求图的最小生成树。



测试说明

平台会对你编写的代码进行测试:

测试输入:

3

lt3.txt

输入说明:

第一行输入3,表示输入图的类型为无向网。

第二行输入文件名,该文件里保存了图的数据信息,内容如下:

5

8

0

1

2

3

4

0 1 1

0 2 3

0 3 4

0 4 7

1 2 2

2 3 5

2 4 8

3 4 6

第1行为图的顶点的个数n;

第2行为图的边的条数m;

第3行至第n+2行是n个顶点的数据;

第n+3行至第n+m+2行是m条边的数据;

预期输出:

无向网

5个顶点8条边。顶点依次是: 0 1 2 3 4

图的邻接矩阵:

∞ 1 3 4 7

1 ∞ 2 ∞ ∞

3 2 ∞ 5 8

4 ∞ 5 ∞ 6

7 ∞ 8 6 ∞

用克鲁斯卡尔构造g的最小生成树的各条边:

边(0,1),权值为1

边(1,2),权值为2

边(0,3),权值为4

边(3,4),权值为6

输出说明:

第一行输出图的类型。

第二行起输出图的顶点和边的数据信息。

最后分行输出用克鲁斯卡尔算法构造的最小生成树的各条边。



代码如下

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h>
#include<limits.h> 

#include"MGraph.h"

#define MAXE 100	


typedef struct
{	int u;							//边的起始顶点
	int v;							//边的终止顶点
	int w;							//边的权值
} Edge;	

void SortEdge(Edge E[],int e);		//对E数组按权值递增排序
void Kruskal(MGraph g);		// 用克鲁斯卡尔算法构造网G的最小生成树T,输出T的各条边  

int main()
{
	MGraph g;	
	CreateGraphF (g);  // 利用数据文件创建无向图
	Display(g);      // 输出无向图 	
	printf("用克鲁斯卡尔构造g的最小生成树的各条边:\n"); 
	Kruskal(g);  // 用克鲁斯卡尔算法构造最小生成树 
	return 0;
}



void SortEdge(Edge E[],int e)		//对E数组按权值递增排序
{
	int i,j,k=0;
	Edge temp;
	for (i=1;i<e;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(MGraph g)			//输出求得的最小生树的所有边
{
	// 用克鲁斯卡尔算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边  
	/********** Begin **********/    	
	int num,i,k=0,t1,t2,j;
	int parent[g.vexnum]; 
	Edge E[100];  	
	for(i = 0; i < g.vexnum; i++){
		parent[i] = -1;
	}
	
	for(i = 0; i < g.vexnum; i++){
		for(j = 0; j<g.vexnum; j++){
			if(i>j&&g.arcs[i][j].adj!=INFINITY){
				E[k].u = i;
				E[k].v = j;
				E[k].w = g.arcs[i][j].adj;
				k++;
			}
			
		}
	}
	SortEdge(E,g.arcnum);
	for(num = 0,i=0;i < g.arcnum; i++){
		t1 = E[i].u;
		t2 = E[i].v;
		while(parent[t1]>-1) {
			t1 = parent[t1];
		}
		while(parent[t2]>-1) {
			t2 = parent[t2];
		}
		if(t1!=t2){
			printf("边(%s,%s),权值为%d\n",g.vexs[E[i].u],g.vexs[E[i].v],g.arcs[E[i].u][E[i].v].adj);
			parent[t1] = t2;
			num++;
			if(num == g.vexnum-1)
				return;
		}
		
	}
	/********** End **********/
}



第4关:求图(邻接表存储)最小生成树的克鲁斯卡尔(Kruskal)算法



任务描述

本关任务:图的存储结构为邻接表,要求编写函数利用克鲁斯卡尔(Kruskal)算法求图的最小生成树。



测试说明

平台会对你编写的代码进行测试:

测试输入:

3

lt3.txt

输入说明:

第一行输入3,表示输入图的类型为无向网。

第二行输入文件名,该文件里保存了图的数据信息,内容如下:

5

8

0

1

2

3

4

0 1 1

0 2 3

0 3 4

0 4 7

1 2 2

2 3 5

2 4 8

3 4 6

第1行为图的顶点的个数n;

第2行为图的边的条数m;

第3行至第n+2行是n个顶点的数据;

第n+3行至第n+m+2行是m条边的数据;

预期输出:

无向网

5个顶点8条边。顶点依次是: 0 1 2 3 4

图的邻接矩阵:

∞ 1 3 4 7

1 ∞ 2 ∞ ∞

3 2 ∞ 5 8

4 ∞ 5 ∞ 6

7 ∞ 8 6 ∞

用克鲁斯卡尔构造g的最小生成树的各条边:

边(0,1),权值为1

边(1,2),权值为2

边(0,3),权值为4

边(3,4),权值为6

输出说明:

第一行输出图的类型。

第二行起输出图的顶点和边的数据信息。

最后分行输出用克鲁斯卡尔算法构造的最小生成树的各条边。



代码如下

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h>
#include<limits.h> 

#include"ALGraph.h"

#define INFINITY 4270000 // 用整型最大值代替∞ 	
#define MAXE 100    // 定义边数最大值

typedef struct
{	int u;							//边的起始顶点
	int v;							//边的终止顶点
	int w;							//边的权值
} Edge;	

void SortEdge(Edge E[],int e);		//对E数组按权值递增排序
void Kruskal(ALGraph g);		// 用克鲁斯卡尔算法构造网G的最小生成树T,输出T的各条边  


int main()
{
	ALGraph g;	
	CreateGraphF (g);  // 利用数据文件创建无向图
	Display(g);      // 输出无向图 	
	printf("用克鲁斯卡尔构造g的最小生成树的各条边:\n"); 
	Kruskal(g);  // 用克鲁斯卡尔算法构造最小生成树 
	return 0;
}



void SortEdge(Edge E[], int e)        //对E数组按权值递增排序
{
    Edge a[20];  
    int i, j, k = 0, n = 0;
    Edge temp;
    for (i = 1;i < e;i++)   //对E数组按权值递增排序,无向网的同一条边被读取了两次
    {
        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]
    }
    
    for (i = 0;i < e ;i++) //在按权值递增有序的E数组中删去多余的一条边
    {
        for (j = i;j < e;j++)
        {
            if (E[j].u == E[i].v && E[j].v == E[i].u)
            {
                a[n] = E[i];
                n++;                
            }
        }
    }    
    for (i = 0;i < n;i++)
    {
        E[i] = a[i];
    }
}


 void Kruskal(ALGraph g)			//输出求得的最小生树的所有边
{
	// 用克鲁斯卡尔算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边  
	/********** Begin **********/    	
	int i,j,u1,v1,sn1,sn2,k;  
    int vset[MAX_VERTEX_NUM];                //建立数组vset  
    Edge E[MAXE];                    //建立存放所有边的数组E  
    k=0;                            //E数组的下标从0开始计  
    ArcNode *p;  
    for(i=0;i<g.vexnum;i++)  
    {  
        p=g.vertices[i].firstarc;  
        while(p)  
        {  
            E[k].u = i;  
            E[k].v = p->data.adjvex;  
            E[k].w = p->data.info;              
            p = p->nextarc;  
            k++;       //累计边数,无向网每条边被累计两次  
        }          
    }      
    SortEdge(E,k);                    //采用直接插入排序对E数组按权值递增排序,要保证相同的边在数组中相邻  
    for (i=0;i<g.vexnum ;i++) vset[i]=i;    //初始化辅助数组  
    k=1;                            //k表示当前构造生成树的第几条边,初值为1  
    j=0;                            //E中边的下标,初值为0  
    while (k<g.vexnum )                    //生成的边数小于n时循环  
    {    u1=E[j].u;   
        v1=E[j].v;        //取一条边的头尾顶点  
        sn1=vset[u1];  
        sn2=vset[v1];                //分别得到两个顶点所属的集合编号  
        if (sn1!=sn2)                //两顶点属于不同的集合,该边是最小生成树的一条边  
        {  
            printf("边(%s,%s),权值为%d\n",g.vertices [u1].data ,g.vertices [v1].data ,E[j].w);  
            k++;                    //生成边数增1  
            for (i=0;i<g.vexnum ;i++)        //两个集合统一编号  
                if (vset[i]==sn2)    //集合编号为sn2的改为sn1  
                    vset[i]=sn1;  
        }  
        j++;              
    }  
	/********** End **********/
}



辅助文件


lt.txt

6
9
武汉
上海
长沙
南京
成都
广州
武汉 长沙 9
武汉 成都 2
长沙 上海 2
长沙 南京 2
上海 南京 5
上海 广州 4
上海 成都 3
南京 广州 8
成都 广州 6


lt2.txt

7
9
高等数学
程序设计基础
C语言
离散数学
数据结构
编译原理
操作系统 
高等数学 C语言 
高等数学 离散数学 
程序设计基础 数据结构
程序设计基础 C语言
C语言 数据结构
离散数学 数据结构 
离散数学 编译原理
数据结构 编译原理 
数据结构 操作系统


lt3.txt

5
8
0
1
2
3
4
0 1 1
0 2 3
0 3 4
0 4 7
1 2 2
2 3 5
2 4 8
3 4 6


MGraph.h

#ifndef   __MGraph_H__
#define   __MGraph_H__
typedef int VRType;    // 顶点关系类型 
typedef char VertexType[20]; // 顶点类型 
// 图的数组(邻接矩阵)存储表示 
#define INFINITY 4270000 // 用整型最大值代替∞ 
#define MAX_VERTEX_NUM 20 // 最大顶点个数 
typedef enum{DG,DN,UDG,UDN}GraphKind; // {有向图,有向网,无向图,无向网} 

typedef struct
{
	VRType adj; // 顶点关系类型。对无权图,用1(是)或0(否)表示相邻否;对带权图,则为权值 
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 二维数组 

typedef struct      // 图的数组(邻接矩阵)存储 
{
	VertexType vexs[MAX_VERTEX_NUM]; // 顶点向量 
	AdjMatrix arcs; // 邻接矩阵 
	int vexnum,arcnum; // 图的当前顶点数和弧数 
	GraphKind kind; // 图的种类标志 
}MGraph;  

/*邻接矩阵的8个基本操作函数声明*/
int LocateVex(MGraph G,VertexType u);//若图G中存在顶点u,则返回该顶点在图中位置;否则返回-1 
VertexType* GetVex(MGraph G,int v);// 根据图G中某个顶点的序号v,返回该顶点的值
void visit(VertexType i);// 访问输出顶点的值
int FirstAdjVex(MGraph G,VertexType v);// v是图G中某个顶点,返回v的第一个邻接顶点的序号。若顶点v在G中没有邻接顶点,则返回-1 
int NextAdjVex(MGraph G,VertexType v,VertexType w);//v是图G中某个顶点,w是v的邻接顶点,返回v的(相对于w的)下一个邻接顶点的序号,若w是v的最后一个邻接顶点,则返回-1 
void CreateGraphF(MGraph &G);//采用数组(邻接矩阵)表示法,由文件构造无向网G
void DestroyGraph(MGraph &G);//销毁图G 
void Display(MGraph G);//输出邻接矩阵存储表示的图G
#endif


MGraph.cpp

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"MGraph.h"
/*邻接矩阵的8个基本操作函数定义*/
int LocateVex(MGraph G,VertexType u)
{
	//初始条件:图G存在,u和G中顶点有相同特征
	// 操作结果:若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 
	int i;
	for(i=0;i<G.vexnum;++i)
		if(strcmp(u,G.vexs[i]) == 0)	
			return i;   // VertexType是char [16]类型
	return -1;	
}

VertexType* GetVex(MGraph G,int v)
{ 
	// 初始条件:图G存在,v是G中某个顶点的序号。操作结果:返回v的值
	if( v>=G.vexnum || v<0 )
		exit(0);
	return &(G.vexs[v]);
}

void visit(VertexType i)
{
	printf("%s ",i);
}

int FirstAdjVex(MGraph G,VertexType v)
{
	 // 初始条件:图G存在,v是G中某个顶点 
	// 操作结果:返回v的第一个邻接顶点的序号。若顶点在G中没有邻接顶点,则返回-1 
	int i,j=0,k;
	k=LocateVex(G,v); // k为顶点v在图G中的序号 
	if(G.kind%2) // 网 
		j=INFINITY;
	for(i=0;i<G.vexnum;i++)
		if(G.arcs[k][i].adj!=j)
			return i;
	return -1;
}

int NextAdjVex(MGraph G,VertexType v,VertexType w)
{
	// 初始条件:图G存在,v是G中某个顶点,w是v的邻接顶点 
	// 操作结果:返回v的(相对于w的)下一个邻接顶点的序号,若w是v的最后一个邻接顶点,则返回-1 
	int i,j=0,k1,k2;
	k1=LocateVex(G,v); // k1为顶点v在图G中的序号 
	k2=LocateVex(G,w); // k2为顶点w在图G中的序号 
	if(G.kind%2) // 网 
		j=INFINITY;
	for(i=k2+1;i<G.vexnum;i++)
		if(G.arcs[k1][i].adj!=j)
			return i;
	return -1;
}

void CreateGraphF(MGraph &G)
{
	// 采用数组(邻接矩阵)表示法,由文件构造无向网G
	int i,j,k,w;
	char filename[13];
	VertexType va,vb;
	FILE *graphlist;
	//printf("请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): ");
	scanf("%d",&G.kind);
	//printf("请输入数据文件名:");
	scanf("%s",filename);   
	graphlist=fopen(filename,"r");       // 以graphlist指针 打开数据文件
	fscanf(graphlist,"%d",&G.vexnum);
	fscanf(graphlist,"%d",&G.arcnum);
	for(i=0;i<G.vexnum;++i)              // 构造顶点向量
		fscanf(graphlist,"%s",G.vexs[i]);
	for(i=0;i<G.vexnum;++i)              // 初始化邻接矩阵
		for(j=0;j<G.vexnum;++j)
		{
			if(G.kind%2)                 // 网
				G.arcs[i][j].adj=INFINITY;       
			else                         // 图
				G.arcs[i][j].adj=0; 
		}
		for(k=0;k<G.arcnum;++k)
		{
			if(G.kind%2)                 // 网
				fscanf(graphlist,"%s%s%d",va,vb,&w);
			else                         // 图
				fscanf(graphlist,"%s%s",va,vb);

			i=LocateVex(G,va);
			j=LocateVex(G,vb);
			if(G.kind == 0)              // 有向图
				G.arcs[i][j].adj =1;
			else if(G.kind == 1)
				G.arcs[i][j].adj=w;      // 有向网
			else   if(G.kind == 2)       // 无向图
				G.arcs[i][j].adj =  G.arcs[j][i].adj=1;
			else
				G.arcs[i][j].adj =  G.arcs[j][i].adj = w;
		}
	fclose(graphlist);              // 关闭数据文件
}

void DestroyGraph(MGraph &G)
{ 
	// 初始条件:图G存在。操作结果:销毁图G 	
	int i,j,k=0;
	if(G.kind%2) // 网 
		k=INFINITY; // k为两顶点之间无边或弧时邻接矩阵元素的值 
	G.vexnum=0; // 顶点数为0 
	G.arcnum=0; // 边数为0 
}

void Display(MGraph G)
{ 
	// 输出邻接矩阵存储表示的图G	
	int i,j;
	switch(G.kind)
	{
	case DG: printf("有向图\n");	      break;
	case DN: printf("有向网\n");          break;
	case UDG:printf("无向图\n");         break;
	case UDN:printf("无向网\n");
	}
	printf("%d个顶点%d条边。顶点依次是: ",G.vexnum,G.arcnum);
	for(i=0;i<G.vexnum;++i)         // 输出G.vexs 
		printf("%s ",G.vexs[i]);
	printf("\n图的邻接矩阵:\n");     // 输出G.arcs.adj 

	for(i=0;i<G.vexnum;i++)
	{
		for(j=0;j<G.vexnum;j++)
			if(G.kind%2)  
			{
				if(G.arcs[i][j].adj==INFINITY)
					printf("%s\t","∞");
				else
					printf("%d\t",G.arcs[i][j].adj);
			}
			else
				printf("%d\t",G.arcs[i][j].adj);
		printf("\n");
	} 
}


ALGraph.h

#ifndef   __ALGraph_H__
#define   __ALGraph_H__

typedef char VertexType[20]; // 顶点类型为字符串 
#define MAX_VERTEX_NUM  20
typedef enum{DG,DN,UDG,UDN}GraphKind; // {有向图,有向网,无向图,无向网} 

typedef struct 
{
	int adjvex; // 该弧所指向的顶点的位置 
	int info; // 网的权值指针 
}ElemType;

typedef struct ArcNode 
{
	ElemType data; // 除指针以外的部分都属于ElemType 
	struct ArcNode *nextarc; // 指向下一条弧的指针 
}ArcNode;           // 表结点 

typedef struct
{
	VertexType data; // 顶点信息 
	ArcNode *firstarc; // 第一个表结点的地址,指向第一条依附该顶点的弧的指针 
}VNode,AdjList[MAX_VERTEX_NUM]; // 头结点 

typedef struct
{
	AdjList vertices;
	int vexnum,arcnum;       // 图的当前顶点数和弧数 
	GraphKind kind;          // 图的种类标志 
}ALGraph;

#define LNode ArcNode        // 定义单链表的结点类型是图的表结点的类型 
#define next nextarc         // 定义单链表结点的指针域是表结点指向下一条弧的指针域 
typedef ArcNode *LinkList;   // 定义指向单链表结点的指针是指向图的表结点的指针 


int equal(ElemType a,ElemType b);
void visit(VertexType i);
int LocateVex(ALGraph G,VertexType u);//若G中存在顶点u,则返回该顶点在图中位置;否则返回-1
int FirstAdjVex(ALGraph G,VertexType v); // 返回v的第一个邻接顶点的序号;否则返回-1 
int NextAdjVex(ALGraph G,VertexType v,VertexType w);//v是图G中某个顶点,w是v的邻接顶点,返回v的(相对于w的)下一个邻接顶点的序号
void CreateGraphF(ALGraph &G);// 采用邻接表存储结构,由文件构造没有相关信息图或网G
void Display(ALGraph G);  // 输出图的邻接表G 


#endif


ALGraph.cpp

#include<stdio.h>
#include<string.h>
#include"ALGraph.h" 
#include"LinkList.h"  



int LocateVex(ALGraph G,VertexType u)
{   // 初始条件:图G存在,u和G中顶点有相同特征 
	// 操作结果:若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 	
	int i;
	for(i=0;i<G.vexnum;++i)
		if(strcmp(u,G.vertices[i].data)==0)
			return i;
	return -1;
}

int FirstAdjVex(ALGraph G,VertexType v)
{   // 初始条件:图G存在,v是G中某个顶点 
	// 操作结果:返回v的第一个邻接顶点的序号。若顶点在G中没有邻接顶点,则返回-1 
	LinkList p;
	int v1;
	v1=LocateVex(G,v); // v1为顶点v在图G中的序号 
	p=G.vertices[v1].firstarc;
	if(p)
		return p->data.adjvex;
	else
		return -1;
}

int NextAdjVex(ALGraph G,VertexType v,VertexType w)
{   // 初始条件:图G存在,v是G中某个顶点,w是v的邻接顶点 
	// 操作结果:返回v的(相对于w的)下一个邻接顶点的序号。若w是v的最后一个邻接点,则返回-1 
	LinkList p,p1; // p1在Point()中用作辅助指针
	ElemType e;
	int v1;
	v1=LocateVex(G,v);        // v1为顶点v在图G中的序号 
	e.adjvex=LocateVex(G,w);  // e.adjvex为顶点w在图G中的序号 
	p=Point(G.vertices[v1].firstarc,e,equal,p1); // p指向顶点v的链表中邻接顶点为w的结点 
	if(!p||!p->next)          // 没找到w或w是最后一个邻接点 
		return -1;
	else                       // p->data.adjvex==w 
		return p->next->data.adjvex; // 返回v的(相对于w的)下一个邻接顶点的序号 	
}


void CreateGraphF(ALGraph &G)
{   // 采用邻接表 存储结构,由文件构造没有相关信息图或网G(用一个函数构造4种图) 
	int i,j,k,w;            // w是权值 
	VertexType va,vb;       // 连接边或弧的2顶点 
	ElemType e;
	char filename[13];
	FILE *graphlist;
	//printf("请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): ");
	scanf("%d",&G.kind);
	//printf("请输入数据文件名:");
	scanf("%s",filename);   
	graphlist=fopen(filename,"r"); // 以读的方式打开数据文件,并以graphlist表示 
	fscanf(graphlist,"%d",&G.vexnum);
	fscanf(graphlist,"%d",&G.arcnum);
	for(i=0;i<G.vexnum;++i)        // 构造顶点向量 
	{
		fscanf(graphlist,"%s",G.vertices[i].data);
		G.vertices[i].firstarc=NULL; // 初始化与该顶点有关的出弧链表 
	}
	for(k=0;k<G.arcnum;++k)          // 构造相关弧链表 
	{
		if(G.kind%2)                 // 网 
			fscanf(graphlist,"%s%s%d",va,vb,&w);
		else                         // 图 
			fscanf(graphlist,"%s%s",va,vb);
		i=LocateVex(G,va);          // 弧尾 
		j=LocateVex(G,vb);          // 弧头 
		e.info=0;                   // 给待插表结点e赋值,图无权 
		e.adjvex=j;                 // 弧头 
		if(G.kind%2)                // 网 
		{
			e.info = w;
		}
		ListInsert(G.vertices[i].firstarc,1,e); // 插在第i个元素(出弧)的表头
		if(G.kind>=2)  // 无向图或网,产生第2个表结点,并插在第j个元素(入弧)的表头 
		{
			e.adjvex=i;                    // e.info不变,不必再赋值 
			ListInsert(G.vertices[j].firstarc,1,e);   // 插在第j个元素的表头
		}
	}
	fclose(graphlist);                    // 关闭数据文件 
} 


void Display(ALGraph G)
{    // 输出图的邻接表G 
	int i;
	LinkList p;
	switch(G.kind)
	{
      case DG: printf("有向图\n");	      break;
      case DN: printf("有向网\n");          break;
      case UDG:printf("无向图\n");         break;
      case UDN:printf("无向网\n");
	}
	printf("%d个顶点:\n",G.vexnum);
	for(i=0;i<G.vexnum;++i)
		printf("%s ",G.vertices[i].data);
	printf("\n%d条弧(边):\n",G.arcnum);
	for(i=0;i<G.vexnum;i++)
	{
		p=G.vertices[i].firstarc;
		while(p)
		{
			printf("%s→%s ",G.vertices[i].data,G.vertices[p->data.adjvex].data);
			if(G.kind%2)            // 网 
				printf(":%d\t",p->data.info );			
			p=p->nextarc;
		}
		printf("\n");
	}
} 

int equal(ElemType a,ElemType b)
{   
	if(a.adjvex==b.adjvex)
		return 1;
	else
		return 0;
}

void visit(VertexType i)
{
	printf("%s ",i);
}


LinkList.h

#ifndef   __LinkList_H__
#define   __LinkList_H__ // 函数结果状态代码
 #define TRUE 1
 #define FALSE 0
 #define OK 1
 #define ERROR 0

#include"ALGraph.h" 

typedef  LNode * LinkList; // 另一种定义LinkList的方法

// 不带头结点的单链表的部分基本操作(9个)
 #define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的
 void InitList(LinkList &L); 
 void ClearList(LinkList &L);
 int ListEmpty(LinkList L);
 int ListLength(LinkList L);
 int GetElem(LinkList L,int i,ElemType &e);
 int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType));
 int ListInsert(LinkList &L,int i,ElemType e);
 int ListDelete(LinkList &L,int i,ElemType &e);
 void ListTraverse(LinkList L,void(*vi)(ElemType));

 LinkList Point(LinkList L,ElemType e,int(*equal)(ElemType,ElemType),LinkList &p);//查找表L中满足条件的结点。如找到
 
#endif


LinkList.cpp

#include<stdio.h>
#include<stdlib.h>
#include"LinkList.h" 
// 不带头结点的单链表的部分基本操作(9个)
void InitList(LinkList &L)
{ // 操作结果:构造一个空的线性表L
   L=NULL; // 指针为空
}

#define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的
void ClearList(LinkList &L)
{ // 初始条件:线性表L已存在。操作结果:将L重置为空表
   LinkList p;
   while(L) // L不空
   {
     p=L; // p指向首元结点
     L=L->next; // L指向第2个结点(新首元结点)
     free(p); // 释放首元结点
   }
}

int ListEmpty(LinkList L)
{ // 初始条件:单链表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE
   if(L)
     return FALSE;
   else
     return TRUE;
}

int ListLength(LinkList L)
{ // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数
   int i=0;
   LinkList p=L;
   while(p) // p指向结点(没到表尾)
   {
     p=p->next; // p指向下一个结点
     i++;
   }
   return i;
}

int GetElem(LinkList L,int i,ElemType &e)
{ // L为不带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
   int j=1;
   LinkList p=L;
   if(i<1) // i值不合法
     return ERROR;
   while(j<i&&p) // 没到第i个元素,也没到表尾
   {
     j++;
     p=p->next;
   }
   if(j==i) // 存在第i个元素
   {
     e=p->data;
     return OK;
   }
   else
     return ERROR;
}

int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType))
{ // 初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
   // 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。
   //           若这样的数据元素不存在,则返回值为0
   int i=0;
   LinkList p=L;
   while(p)
   {
     i++;
     if(compare(p->data,e)) // 找到这样的数据元素
       return i;
     p=p->next;
   }
   return 0;
}

int ListInsert(LinkList &L,int i,ElemType e)
{ // 在不带头结点的单链线性表L中第i个位置之前插入元素e
   int j=1;
   LinkList p=L,s;
   if(i<1)                // i值不合法
     return ERROR;
   s=(LinkList)malloc(sizeof(LNode)); // 生成新结点
   s->data=e; // 给s的data域赋值
   if(i==1) // 插在表头
   {
     s->next=L;
     L=s; // 改变L
   }
   else
   { // 插在表的其余处
     while(p&&j<i-1) // 寻找第i-1个结点
     {
       p=p->next;
       j++;
     }
     if(!p) // i大于表长+1
       return ERROR;
     s->next=p->next;
     p->next=s;
   }
   return OK;
}

int ListDelete(LinkList &L,int i,ElemType &e)
 { // 在不带头结点的单链线性表L中,删除第i个元素,并由e返回其值
   int j=1;
   LinkList p=L,q;
   if(i==1)    // 删除第1个结点
   {
     L=p->next; // L由第2个结点开始
     e=p->data;
     free(p);  // 删除并释放第1个结点
   }
   else
   {
     while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前趋
     {
       p=p->next;
       j++;
     }
     if(!p->next||j>i-1) // 删除位置不合理
       return ERROR;
     q=p->next;         // 删除并释放结点
     p->next=q->next;
     e=q->data;
     free(q);
   }
   return OK;
}

void ListTraverse(LinkList L,void(*vi)(ElemType))
 { // 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数vi()
   LinkList p=L;
   while(p)
   {
     vi(p->data);
     p=p->next;
   }
   printf("\n");
 } 

 LinkList Point(LinkList L,ElemType e,int(*equal)(ElemType,ElemType),LinkList &p)
{  //查找表L中满足条件的结点。如找到,返回指向该结点的指针,p指向该结点的前驱(若该结点是首元结点,则p=NULL)。
   //如表L中无满足条件的结点,则返回NULL,p无定义。函数equal()的两形参的关键字相等,返回OK;否则返回ERROR
	int i,j;
	i=LocateElem(L,e,equal);
	if(i)                  // 找到 
	{
		if(i==1)          // 是首元结点 
		{
			p=NULL;
			return L;
		}
		p=L;
		for(j=2;j<i;j++)
			p=p->next;
		return p->next;
	}
	return NULL;           // 没找到 
}



版权声明:本文为qq_45917176原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。