数据结构C语言版清华大学严蔚敏(学习总结5)——图的全代码:邻接矩阵、邻接表、十字链表法、多重链表法、最短路径Dijkstra算法、图的遍历、深度优先、广度优先、拓扑排序、最小生成树(另附理解笔记)

 https://blog.csdn.net/weixin_51538341/article/details/122265865?spm=1001.2014.3001.5501

 1、邻接矩阵

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

typedef char datatype;
typedef int edgetype;
#define MAXVEX 20					//顶点最大数,由使用者自定义 
#define INFINITY 65535 				//代指无限大 
typedef struct
{
	datatype data[MAXVEX];			//顶点表 
	edgetype arc[MAXVEX][MAXVEX];	//存储边或弧的权的邻接矩阵 
	int numVertexes,numEdges;		//顶点数,边或弧的个数 
}MGraph;//图结构--邻接矩阵

void Creat_MGraph(MGraph *G);
int main()
{
	MGraph G;
	Creat_MGraph(&G);
	int i,j;
	//将边表进行输出检查
	printf
	for(i=0;i<G.numVertexes;i++)
	{
		for(j=0;j<G.numVertexes;j++)
			printf("%10d",G.arc[i][j]); 
		printf("\n");
	}
}
//使用图的邻接矩阵存储方式创建网图  无向网图
void Creat_MGraph(MGraph *G)
{
	int a,b,i,j;
	int x,y;
	datatype ch;
	edgetype w;
	
	printf("Please input numVertexes,numEdges:");
	scanf("%d,%d",&a,&b);	//a:顶点个数 , b:边或弧的个数
	G->numVertexes=a;
	G->numEdges=b;
	//初始化邻接矩阵 
	for(i=0;i<a;i++)
	{
		for(j=0;j<a;j++)
		{
			if(i==j)
				G->arc[i][j]=0;
			else 
		 		G->arc[i][j]=INFINITY;
		}
	}
	//给每个顶点赋值 
	for(i=0;i<a;i++)
	{
		printf("Please input data[%d]:",i);
		scanf(" %c",&ch);
		G->data[i]=ch;
	}
	//给每条边或弧赋值 
	for(i=0;i<b;i++)
	{
		printf("Please input (vi,vj)的下标,和对应的权:\n");
		scanf(" %d,%d,%d",&x,&y,&w);
		G->arc[x][y]=w; 
		G->arc[y][x]= G->arc[x][y]; // 若为无向图,矩阵对称。否则删除即可 
	}
	//n:顶点数,e:边数 时间复杂度O(n^2+n+e)--------->O(n^2) 
}

2、邻接表

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

typedef char datatype;
typedef int edgetype;
#define MAXVEX 20	

typedef struct ENode
{
	int adjvex;				//当前顶点的邻接点在顶点表中的下标 
	edgetype w;				//权数 
	struct ENode *next;		//指向下一个邻接点 
}EdgeNode;//边表结构 

typedef struct VNode
{
	datatype data;
	EdgeNode *firstedge;	//当前顶点的第一个邻接点,边表头指针 
}VertexNode,AdjList[MAXVEX];//顶点结构 

typedef struct
{
	AdjList adjList;		//储存所有的顶点的数组 
	int numVertexes,numEdges;//图中顶点数,边或弧的个数  
}GraphAdjList;//图结构--邻接表 

void Creat_GraphAdjList(GraphAdjList *G);
int main()
{
	GraphAdjList G;
	Creat_GraphAdjList(&G);
}
//使用图的邻接表存储方式创建网图  无向网图
void Creat_GraphAdjList(GraphAdjList *G)
{
	int v_num,e_num;
	int i,x,y;
	EdgeNode *e;
	datatype ch;
	edgetype w;
	
	printf("Please input numVertexes,numEdges:");
	scanf("%d,%d",&v_num,&e_num);	//顶点个数 , 边或弧的个数
	G->numVertexes=v_num;
	G->numEdges=e_num;
	//给每个顶点赋值 
	for(i=0;i<v_num;i++)
	{
		printf("Please input adjList[%d].data:",i);
		scanf(" %c",&ch);
		G->adjList[i].data=ch;
		G->adjList[i].firstedge=NULL;
	}
	//建立边表 
	/*建立有向图时,输入一次下标时
	只需将其中一个顶点作为弧尾或弧头的形式 进行建立边表
	不用管另一个顶点的边也要反向建立*/
	for(i=0;i<e_num;i++)
	{
		printf("Please input (vi,vj) or <vi,vj>的下标,和对应的权:\n");
		scanf(" %d,%d,%d",&x,&y,&w);
		e=(EdgeNode*)malloc(sizeof(EdgeNode));
		e->adjvex=y;
		e->w=w;
		e->next=G->adjList[x].firstedge;	//以头插法的形式将边插入到边表中 
		G->adjList[x].firstedge=e;
		
		e=(EdgeNode*)malloc(sizeof(EdgeNode));
		e->adjvex=x;
		e->w=w;
		e->next=G->adjList[y].firstedge;
		G->adjList[y].firstedge=e;
	}
	//n:顶点数,e:边数 时间复杂度--------->O(n+e) 
	
 	//打印邻接表
    printf("邻接表为:\n");
    for(i=0;i<G->numVertexes;i++){
        e=G->adjList[i].firstedge;
        while(e){
            printf("(%c,%c)",G->adjList[i].data,G->adjList[e->adjvex].data);
            e=e->next;
        }
        printf("\n");
    } 
}

3、十字链表法

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

typedef char datatype;
typedef int edgetype;	//权值类型,建立网图时需引进 
#define MAXVEX 20	

typedef struct ENode
{
	int tailvex,headvex;	//弧起点在顶点表的下标,弧终点在顶点表的下标  
	struct ENode *taillink,*headlink;	//指向起点相同的下条边,指向终点相同的下条边
}EdgeNode;//边表结构 

typedef struct VNode
{
	datatype data;
	EdgeNode *firstin,*firstout;//入边表头指针 ,出边表头指针 
}VertexNode,OrtList[MAXVEX];//顶点结构 

typedef struct
{
	OrtList OrtList;		//储存所有的顶点的数组 
	int numVertexes,numEdges;//图中顶点数,边或弧的个数  
}GraphOrtList;//图结构--邻接表 

void Creat_GraphOrtList(GraphOrtList *G);
void Print_GraphOrtList(GraphOrtList G);

int main()
{
	GraphOrtList G;
	Creat_GraphOrtList(&G);
	Print_GraphOrtList(G);
}
//使用图的十字链表存储方式创建图  针对有向图的优化 
void Creat_GraphOrtList(GraphOrtList *G)
{
	int v_num,e_num;
	int i,x,y;
	EdgeNode *e;
	datatype ch;
	
	printf("Please input numVertexes,numEdges:");
	scanf("%d,%d",&v_num,&e_num);	//顶点个数 , 边或弧的个数
	G->numVertexes=v_num;
	G->numEdges=e_num;
	//给每个顶点赋值 
	for(i=0;i<v_num;i++)
	{
		printf("Please input adjList[%d].data:",i);
		scanf(" %c",&ch);
		G->OrtList[i].data=ch;
		G->OrtList[i].firstin=NULL;
		G->OrtList[i].firstout=NULL;	//入边表头指针,出边表头指针赋值NULL 
	}
	//建立边表 
	for(i=0;i<e_num;i++)
	{
		printf("Please input <vi,vj>的下标:\n");
		scanf(" %d,%d",&x,&y);
		e=(EdgeNode*)malloc(sizeof(EdgeNode));
		e->tailvex=x;
		e->headvex=y;
		//firstin:入边表头指针 firstout:出边表头指针
		e->taillink=G->OrtList[x].firstout;//taillink:指向起点相同的下条边
		G->OrtList[x].firstout=e;
		e->headlink=G->OrtList[y].firstin;//headlink:指向终点相同的下条边
		G->OrtList[y].firstin=e;
	}
	//n:顶点数,e:边数 时间复杂度--------->O(n+e) 
}
//打印十字链表 
void Print_GraphOrtList(GraphOrtList G)
{
	int i;
	EdgeNode *p;
    printf("邻接表为:\n");
    for(i=0;i<G.numVertexes;i++){
        p=G.OrtList[i].firstout;
        while(p){
            printf("(%c,%c)",G.OrtList[i].data,G.OrtList[p->headvex].data);
            p=p->taillink;
        }
        printf("\n");
    } 

    printf("逆邻接表为:\n");
    for(i=0;i<G.numVertexes;i++){
        p=G.OrtList[i].firstin;
        while(p){
            printf("(%c,%c)",G.OrtList[p->tailvex].data,G.OrtList[i].data);
            p=p->headlink;
        }
        printf("\n");
    } 
}

4、邻接多重表

#include<stdio.h>
#include<stdlib.h>
#define MAX_SIZE 20

typedef struct EdgeNode	//边结点
{
	int info;								//边的数据信息
	int mark;								//访问标记
	int iVex, jVex;							//边所连接的两个顶点
	struct EdgeNode *tailEdge, *headEdge;	//分别指向头、尾顶点的边链表
}EdgeNode;

typedef struct VexNode	//顶点结点
{
	char data;								//顶点数据信息
	EdgeNode *firstEdge ;					//分别指向第一个条边
}VexNode;

typedef struct
{
	VexNode MutiList[MAX_SIZE];		//顶点数组
	int numVexs, numEdge;			//图的顶点数和边数
}AMLGraph;

void CreateAMLGraph(AMLGraph *G)
{
	int i, j, k, w;
	printf("请输入顶点数和边数:");
	scanf("%d %d", &G->numVexs, &G->numEdge);

	for(i = 0; i < G->numVexs; i++)
	{
		fflush(stdin);
		printf("请输入第%d个顶点信息:", i+1);
		scanf("%c", &G->MutiList[i].data);
		G->MutiList[i].firstEdge = NULL;
	}
	printf("\n");
	for(k = 0; k < G->numEdge; k++)
	{
		printf("请输入弧(Ai, Aj)的头尾顶点及其权值:");
		scanf("%d %d %d", &i, &j, &w);
		//创建新的弧结点
	 	EdgeNode *s;
		s =  (EdgeNode *)malloc(sizeof(EdgeNode));
		s->info = w;
		s->mark = 0;
		s->jVex = i - 1;
		s->iVex = j - 1;
		//头插法插入,(插入边结点链表<——头顶点i——尾顶点j)
		s->headEdge = G->MutiList[i-1].firstEdge;
		G->MutiList[i-1].firstEdge = s;
		//头插法插入,(头顶点i——尾顶点j——>插入边结点链表)
		s->tailEdge = G->MutiList[j-1].firstEdge;
		G->MutiList[j-1].firstEdge = s;
	}
}
void TraverseEdge(AMLGraph &G)	//遍历各个顶点的所有连接着的边
{
	EdgeNode *p;
	printf("图G:\n");
	for(int i = 0; i < G.numVexs; i++)
	{
		p = G.MutiList[i].firstEdge;
		while (p && !p->mark)
		{
			printf("%c----%c(%d)\n",G.MutiList[p->jVex].data, G.MutiList[p->iVex].data, p->info);
			p->mark = 1;
			p = p->headEdge;
		}
	}
}

int main()
{
	AMLGraph G;
	CreateAMLGraph(&G);
	TraverseEdge(G);
	return 0;
}

5、最短路径  Dijkstra算法

#include<stdio.h>
#include<stdlib.h>
#define	MAXVEX 100
#define INF 65535

typedef char VertexType;
typedef int EdgeType;

typedef struct		//邻接矩阵结构
{
	VertexType vexs[MAXVEX];
	EdgeType arc[MAXVEX][MAXVEX];
	int numVexs, numEdges;
}AMGraph;

int Flag[MAXVEX];		//标志每个顶点是否加入最短路径的顶点集合S中

int minLen[MAXVEX];		//表示从源顶点到各个顶点最短路径长度

int Path[MAXVEX];		//表示最短路径中每个顶点的前驱顶点

void CreateAMGraph(AMGraph &G)		//创建邻接矩阵
{
	int i, j, k, w;
	printf("请输入顶点数和边数:");
	scanf("%d %d", &G.numVexs, &G.numEdges);
	fflush(stdin);	
	for(i = 0; i < G.numVexs; i++)
	{
		printf("请第%d个顶点信息:", i + 1);
		scanf("%c", &G.vexs[i]);
		fflush(stdin);	
	}
	for(i = 0; i < G.numVexs; i++)
		for(j = 0; j < G.numVexs; j++)
			G.arc[i][j] = INF;		

	for(k = 0; k < G.numEdges; k++)
	{
		printf("请输入第%d条边的两个顶点及其权值:", k+1);
		scanf("%d %d %d", &i, &j, &w);
		G.arc[i-1][j-1] = w;
	}
}


void ShortestPath_DIJ(AMGraph G, int v0)
{
	//初始化
	for(int v = 0; v < G.numVexs; v++)
	{
		Flag[v] = 0;				//初始化所有顶点都未加入集合S
		minLen[v] = G.arc[v0][v];	//初始化v0到各个顶点的最短距离
		if(minLen[v] < INF)			
			Path[v] = v0;			//若有直接通路,则前驱置为v0
		else
			Path[v] = -1;			//若无直接通路,则前驱置为-1
	}
	Flag[v0] = 1;					//源点v0已访问
	minLen[v0] = 0;					//源点到源点的距离为0
	
	//除源点v0外, 访问其他n-1个顶点
	for(int i = 1; i < G.numVexs; i++)	
	{
		int w, min = INF;
		//遍历所有顶点,找出当前最短的一条通路,终点为v
		for(w = 0; w < G.numVexs; w++)		
		{
			if(!Flag[w] && minLen[w] < min)	
			{
				v = w;
				min = minLen[w];
			}
		}
		Flag[v] = 1;	//将顶点v加入最短路径集合S中

		//更新从v0到集合S中所有顶点的最小路径
		for(w = 0; w < G.numVexs; w++)	
		{
			if(!Flag[w] && minLen[v] + G.arc[v][w] < minLen[w])	//比较v0->v->w和v0->w两段路径长度
			{
				minLen[w] =  minLen[v] + G.arc[v][w];			//替换更短的那条路径
				Path[w] = v;									//并更改当前最短路径顶点的前驱
			}
		}
	}
	
}

void DisplayPath(AMGraph G, int Path[], int ve)	//打印路径
{
	int v = ve;
	printf("min = %d\n", minLen[v]);
	printf("%c<--", G.vexs[v]);
	while(Path[v] != -1)
	{
		printf("%c<--", G.vexs[Path[v]]);
		v = Path[v];
	}
	printf("\n");
	
}

int main()
{
	AMGraph G;
	CreateAMGraph(G);

	int v0, ve;
	printf("请输入源点v0和终点ve:");
	scanf("%d %d", &v0, &ve);

	ShortestPath_DIJ(G, v0 - 1);
	DisplayPath(G, Path, ve - 1);

	return 0;
}

6、图的遍历————深度优先

//深度优先遍历 
void DFS(LGraph* g,int *visit,int v)
{
	printf("Node: %d\n",v);
	ENode* p = g->a[v];
	visit[v] =1;
	for(;p;p = p->nextArc)
	{
		if(visit[p->adjVex]==0)
			DFS(g,visit,p->adjVex);
	}
}

void DFSGraph(LGraph* g)
{
	int *visit = (int*)malloc(sizeof(int)*g->n);
	int i  = 0;
	for(;i<g->n;i++)
		visit[i]= 0;
	
	for(i = 0;i<g->n;i++)
	{
		if(visit[i]==0)
			DFS(g,visit,i);
	}
	free(visit);
 } 

7、图的遍历————广度优先

//广度优先遍历
 void BFS(LGraph* g,int v,int* visit)
 {
 	ENode *q[50];
 	int front = 0,rear = 0;
 	visit[v] = 1;
 	printf("Node: %d\n",v);
 	
 	q[rear] = g->a[v];
 	rear = (rear+1)%10;
 	while(front!=rear)
 	{
 		ENode* p = q[front];
 		front = (front+1)%10;
 		for(;p;p = p->nextArc)
 		{
 			if(visit[p->adjVex]==0)
 			{
 				visit[p->adjVex]=1;
 				printf("Node: %d\n",p->adjVex);
 				q[rear] = g->a[p->adjVex];
 				rear = (rear+1)%10;
			 }
		 }
	 }
  }
 
 void BFSGraph(LGraph* g)
 {
 	int *visit = (int*)malloc(sizeof(int)*g->n);
	int i  = 0;
	for(;i<g->n;i++)
		visit[i]= 0;
	
	for(i = 0;i<g->n;i++)
	{
		if(visit[i]==0)
			BFS(g,i,visit);
	}
	free(visit);
  } 

8、AOV网的——拓扑排序

#include"stdio.h"
#include"stdlib.h"
#define M 20
typedef char vertextype;
typedef struct node  //边结点类型定义
{
	int adjvex;
	struct node *next;
}EdgeNode;
typedef struct   //带顶点入度的头结点定义
{
	EdgeNode *firstedge;
	vertextype vertex;
	int id;   //顶点的入度域
}VertexNode;
typedef struct   //AOV网的邻接表结构
{
	VertexNode adjlist[M];
	int n,e;   //图的顶点数与边数
}AovGraph;
void creataov(AovGraph *g)   //建立AOV网的邻接表 
{
	 int i,j,k;
	 EdgeNode *s;
	 printf("输入图的顶点数与边数:");
	 scanf("%d%d",&g->n,&g->e);
	 printf("输入顶点及顶点的入度\n");
	 for(i=0;i<g->n;i++)
	 {
		  scanf("%ls%d",&g->adjlist[i].vertex,&g->adjlist[i].id);
		  g->adjlist[i].firstedge=NULL;
	 }
	 printf("输入有序对\n");
	 for(k=0;k<g->e;k++)
	 {
		  scanf("%d%d",&i,&j);
		  s=(EdgeNode*)malloc(sizeof(EdgeNode));
		  s->adjvex=j;
		  s->next=g->adjlist[i].firstedge;
		  g->adjlist[i].firstedge=s;
	}
}
void TopSort(Aovgraph g)  //AOV网拓扑排序 
{
	 int i,j,v,flag[M],queue[M],rear,front;
	 EdgeNode *p;
	 front=rear=0;  //初始化空队列
	 for(i=0;i<g.n;i++)
	  flag[i]=0;    //初始化访问标记
	 for(i=0;i<g.n;i++)    //入度为0的结点进队
	 {
		  if(g.adjlist[i].id==0&&flag[i]==0)
		  {
			   queue[rear++]=i;   
			   flag[i]=1;
		  } 
	 }
	 printf("输出AOV网的拓扑序列:");
	 while(front<rear)  //队列不为空 
	 {
		  v=queue[front++];   //队首元出队 
		  printf("%c ",g.adjlist[v].vertex);
		  p=g.adjlist[v].firstedge;
		while(p)   //所有与v邻接的顶点的入度减1 
		 {
			  j=p->adjvex;
			  if(--g.adjlist[j].id==0&&flag[j]==0)  //入度为0则进队 
			   {
				    queue[rear++]=j;
				    flag[j]=1;
			   }
			   p=p->next;
		 }  
	 }
}
int main()
{
	AovGraph ag;
	printf("创建一个AOV网的邻接表\n"); 
 	creataov(&ag);
 	TopSort(ag);
 	return 0;
}

9、最小生成树  prim算法

#include<stdio.h>

#define	MAXVEX 100
#define INF 65535

typedef char VertexType;
typedef int EdgeType;

typedef struct		//邻接矩阵结构
{
	VertexType vexs[MAXVEX];
	EdgeType arc[MAXVEX][MAXVEX];
	int numVexs, numEdges;
}AMGraph;

typedef struct		//最小边的结构
{
	VertexType adjvex;	//最小边所对应的生成树内顶点
	EdgeType lowcost;	//最小边上的权值
}MinEdge;

MinEdge closedge[MAXVEX];	//定义最小边数组

void CreateAMGraph(AMGraph &G)		//创建邻接矩阵
{
	int i, j, k, w;
	printf("请输入顶点数和边数:");
	scanf("%d %d", &G.numVexs, &G.numEdges);
	fflush(stdin);	
	for(i = 0; i < G.numVexs; i++)
	{
		printf("请第%d个顶点信息:", i + 1);
		scanf("%c", &G.vexs[i]);
		fflush(stdin);	
	}
	for(i = 0; i < G.numVexs; i++)
		for(j = 0; j < G.numVexs; j++)
			G.arc[i][j] = INF;		

	for(k = 0; k < G.numEdges; k++)
	{
		printf("请输入第%d条边的两个顶点及其权值:", k+1);
		scanf("%d %d %d", &i, &j, &w);
		G.arc[i-1][j-1] = w;
		G.arc[j-1][i-1] = w;
	}
}

int LocateAMGraph(AMGraph G, VertexType u)	//取顶点u的位置
{
	for(int i = 0; i < G.numVexs; i++)
	{
		if(G.vexs[i] == u)
			return i;
	}
	return -1;
}

int Min(MinEdge closedge[], int size)		//取最小权值边的顶点位置
{
	int k = -1;
	for(int i = 0; i < size; i++)
	{
		if(closedge[i].lowcost > 0)
		{
			int min = closedge[i].lowcost;
			for(int j = i; j < size; j++)
			{
				if(closedge[j].lowcost > 0  && min >= closedge[j].lowcost)
				{
					min = closedge[j].lowcost;
					k = j;
				}
			}
			break;
		}
	}
	return k;
}

void Display(AMGraph G, MinEdge closedge[])		//遍历最小边数组和顶点
{
	for(int i = 0; i < G.numVexs; i++)	
	{
		printf("%c\t", closedge[i].adjvex);
	}
	printf("\n");
	for(int j = 0; j < G.numVexs; j++)
	{
		printf("%c\t", G.vexs[j]);
	}
	printf("\n");
	for(int k = 0; k < G.numVexs; k++)
	{
		if(closedge[k].lowcost == INF)
			printf("∞\t");
		else
			printf("%d\t", closedge[k].lowcost);
	}
	printf("\n");
}

void MiniSpanTree_Prim(AMGraph G, VertexType u)		//最小生成树Prim算法
{
	int i, j, k = LocateAMGraph(G, u);
	//初始化每个顶点到顶点u的最小边权值
	for(i = 0; i < G.numVexs; i++)
	{
		closedge[i].adjvex = u;
		closedge[i].lowcost = G.arc[k][i];
	}
	//边权值置为0,表示该顶点已加入最小生成树中
	closedge[k].lowcost = 0;
	
	//Display(G, closedge);
	//printf("最小边数组初始化完成!\n");
	
	//对剩余的n-1条边进行操作
	for(i = 1; i < G.numVexs; i++)
	{
		//取最小边的顶点位置
		k = Min(closedge, G.numVexs);
		printf("---(%d)--->%c\n", closedge[k].lowcost, G.vexs[k]);
		
		//边权值置为0,表示该顶点已加入最小生成树中
		closedge[k].lowcost = 0;
		
		//更新边的权值,使得生成树内顶点到其他顶点的边值最小
		for(j = 0; j < G.numVexs; j++)
		{
			if(G.arc[k][j] < closedge[j].lowcost)
			{
				closedge[j].adjvex = G.vexs[k];
				closedge[j].lowcost = G.arc[k][j];
			}
		}
	}
}
int main()
{
	AMGraph G;
	CreateAMGraph(G);
	//从顶点'A'开始创建最小生成树
	MiniSpanTree_Prim(G, 'A');
	return 0;
}

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玛卡巴卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值