图的基本操作

图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。

下面我主要讲一下图的创建以及对图进行操作的一些基础算法:

首先来讲图的创建,图主要有两种存储形式,邻接矩阵和邻接表。下面给出这两种存储方式的结构:

//图的邻接矩阵存储形式 
typedef struct{
	int no;				//顶点标号(默认为1-n) 
	InfoType *info;		        //存储顶点信息(比如顶点的名字)
}VertexType;

typedef struct{    
	VertexType vex[MAX_VERTEX_NUM];	        	    //顶点向量 
	int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];        //邻接矩阵数组,相邻就是1,不相邻就是0,也可以存储权值 
	int n,e;		                       //图的当前顶点数和弧数 
}MGraph;

//图的邻接表存储形式
typedef struct ArcNode{		//链表节点 
	int adjvex;			//该弧所指向的顶点的位置 
	int weight;			//网的权值 
	ArcNode *nextarc;		//指向下一条弧的指针 
}ArcNode; 

typedef struct{
	VertexType data;		//顶点信息:标号和其他信息 
	ArcNode *fristarc;		//第一个表节点的地址,指向第一条依附该顶点的弧的指针 
}VNode;

typedef struct{
	VNode adjlist[MAX_VERTEX_NUM];	//单链表的个数(顶点的个数) 
	int n,e;		        //邻接表存储的图的当前顶点数和弧数
}AlGraph;

看懂了这两种存储结构都,让我们来思考一下怎么创建这两种存储方式呢?

先来看一下邻接矩阵的创建:

Status CreateMat(MGraph &G)			//创建一个邻接矩阵,只需输入点与点之间的关系即可 
{
	//char iofo1;
	int v1,v2,w,bh;
	cin>>G.n>>G.e;					//先数一数顶点数和边数 
	for(int i=1;i<=G.n;i++)
	{
		//cin>>bh;
		G.vex[i].no = i;			//给G.n个顶点标号为1-n,也可以自定义标号bh
		//cin>>info1;				//存储一些顶点信息,比如"青岛" 
		//G.vex[i].info = info1;
		for(int j=1;j<=G.n;j++)	        	//给矩阵赋初值0 
			G.arcs[i][j] = 0;
	}
	for(int i=1;i<=G.e;i++)		 
	{
		cin>>v1>>v2>>w;				//输入顶点向量和两个顶点向量之间的关系 
		G.arcs[v1][v2] = w;			//存储弧度信息,权值或者存1表示两顶点向量相邻 
		G.arcs[v2][v1] = w;
	}
}

再来看一下邻接表的创建:

Status CreateList(AlGraph &AGG)			//创建一个邻接表 
{
	int t,sum_e=0;
	ArcNode *p;
	cin>>AGG.n;				//输入邻接表的顶点数 
	for(int i=1;i<=AGG.n;i++)
	{
		AGG.adjlist[i].fristarc = NULL;	//给邻接表中的所有头结点的指针域赋初值 
		AGG.adjlist[i].data.no = i;
		cin>>t;
		while(t != 0)
		{
			p = (ArcNode *)malloc(sizeof(ArcNode));
			p->adjvex = t;
			p->nextarc = AGG.adjlist[i].fristarc;
			AGG.adjlist[i].fristarc = p;
			sum_e++;		//记录弧数的二倍,因为两点之间连接了两次 
			cin>>t;
		}
	}
	AGG.e = sum_e/2;
}

学会了两种存储方式的结构的创建和各变量所存储的内容后,下面来看一下两种存储方式之间的转化。

①邻接矩阵转变为邻接表:

Status MatToList(MGraph G,AlGraph &AG)	//将邻接矩阵转为邻接表 
{
	ArcNode *p;
	AG.n = G.n;				    //将邻接矩阵的顶点数和弧数赋给邻接表 
	AG.e = G.e;
	for(int i=1;i<=G.n;i++)
	{
		AG.adjlist[i].fristarc = NULL;	    //给邻接表中的所有头结点的指针域赋初值 
		AG.adjlist[i].data.no = G.vex[i].no;//将邻接矩阵的编号赋给邻接表的头节点 
	}
	for(int i=1;i<=G.n;i++)
	{
		for(int j=G.n;j>=1;j--)
		{
			if(G.arcs[i][j]!=0)	    //如果两个点之间相连,就用头插法插入节点信息 
			{
				 p = (ArcNode *)malloc(sizeof(ArcNode));
				 p->adjvex = j;	    //顶点的位置(几号) 
				 p->nextarc = AG.adjlist[i].fristarc;
				 AG.adjlist[i].fristarc = p;
			}
		}
	}
}

②邻接表转变为邻接矩阵:

Status ListToMat(MGraph &GG,AlGraph AGG)	//将邻接表转为邻接矩阵 
{
	ArcNode *p;
	GG.n = AGG.n;
	GG.e = AGG.e;
	
	for(int i=1;i<=GG.n;i++)		//初始化邻接矩阵 
		for(int j=1;j<=GG.n;j++)
			GG.arcs[i][j] = 0;

	for(int i=1;i<=AGG.n;i++)		//将每一顶点相连的点创建为一个个的单链表 
	{
		p = AGG.adjlist[i].fristarc;
		while(p)
		{
			GG.arcs[i][p->adjvex] = 1;
			p = p->nextarc;
		}
	}
}

创建完毕之后我们想要看一看我们存储的图是不是我们想要的,下面就要用到打印了。

邻接矩阵和邻接表的打印:

void DisMat(MGraph G)					//打印邻接矩阵 
{
	cout<<"该邻接矩阵为:"<<endl; 
	for(int i=1;i<=G.n;i++)
	{
		for(int j=1;j<=G.n;j++)
			cout<<G.arcs[i][j]<<" ";
		cout<<endl;
	}
}

void DisList(AlGraph AG)				//打印邻接表 
{
	cout<<"该邻接表为:"<<endl; 
	ArcNode *p;
	for(int i=1;i<=AG.n;i++)
	{
		p = AG.adjlist[i].fristarc;    
		cout<<AG.adjlist[i].data.no;	        //打印节点的标号 
		while(p)
		{
			cout<<"->"<<p->adjvex;
			p = p->nextarc;
		}
		cout<<endl;
	}
}

最后等你完成所以的算法之后,销毁这个线性表:

void Destory_List(AlGraph &AGG)		//销毁图的邻接表 
{
	ArcNode *p,*q;
	for(int i=1;i<=AGG.n;i++)
	{
		p = AGG.adjlist[i].fristarc;
		while(p)
		{
			q = p->nextarc;
			free(p);
			p = q;
		}
	}
}

图的两种搜索方式:深度优先搜索DFS和广度优先搜索BFS。

void DFS(AlGraph AG,int v)				//深度优先搜索遍历邻接表 
{
	cout<<AG.adjlist[v].data.no<<"  ";		//先输出每个顶点的标号 
	d[v] = 1;					//已经遍历过的顶点标记为 1 
	ArcNode *p;
	p = AG.adjlist[v].fristarc;			//创建一个新节点怕,并指向编号为v的头结点 
	
	while(p != NULL)
	{
		if(!d[p->adjvex])		        //如果该顶点没有被标记过,就该顶点的单链表 
			DFS(AG,p->adjvex);
		p=p->nextarc;
	}
}

void BFS(AlGraph AG,int v)		    	        //广度优先搜索遍历邻接表 
{
	cout<<AG.adjlist[v].data.no<<"  ";		//先输出每个顶点的标号
	b[v] = 1;					//已经遍历过的顶点标记为 1
	q.push(v);
	ArcNode *p;
	p = AG.adjlist[v].fristarc;			//创建一个新节点,并指向编号为v的头结点
	
	while(p!=NULL)					//将顶点标号为v的单链表中的元素全部入队列
	{					    //也就是将你想遍历的第一个点相邻的节点入队列 
		q.push(p->adjvex);
		p=p->nextarc;
	}
	while(!q.empty())				//只要队列不空,也就是还没有遍历完整个邻接表就不会停止 
	{
		p=AG.adjlist[q.front()].fristarc;	//让p指向队列第一个元素指向的顶点 
		while(p)						//将与顶点p相连的顶点全部入队列 
		{	
			if(!b[p->adjvex])				//如果该顶点没有遍历过 
			{
				q.push(p->adjvex);			//将该顶点入队列 
				cout<<AG.adjlist[p->adjvex].data.no<<"  ";//输出顶点的值 
				b[p->adjvex]=1;				//并标记为 1 
			}
			p=p->nextarc; 
		}
		q.pop();    //将与p相连的所有节点遍历完并入队列后,将该顶点出队列 
	}
	cout<<endl;
}

有关有每个顶点的入度和出度问题以及出度为0的顶点的数量,都写到了一起,看代码:

void InDs(MGraph G)							//以邻接矩阵的形式存储,找每个顶点的入度 
{
	for(int i=1;i<=G.n;i++)
	{
		int sum=0;
		for(int j=1;j<=G.n;j++)
		{
			if(G.arcs[j][i] != 0)
				sum++;
		}
		printf("顶点%d的入度为:%d\n",i,sum);
	}
	cout<<endl; 
}
void OutDs(MGraph G)						//以邻接矩阵的形式存储,找每个顶点的出度 
{
	for(int i=1;i<=G.n;i++)
	{
		int sum=0;
		for(int j=1;j<=G.n;j++)
		{
			if(G.arcs[i][j] != 0)
				sum++;
		}
		printf("顶点%d的出度为:%d\n",i,sum);
	}
	cout<<endl; 
}
void ZeroOutDs(MGraph G)					//以邻接矩阵的形式存储,出度为 0的点的个数 
{
	int temp=0;
	for(int i=1;i<=G.n;i++)
	{
		int sum=0;
		for(int j=1;j<=G.n;j++)
		{
			if(G.arcs[i][j] != 0)
				sum++;
		}
		if(sum == 0)
			temp++;
	}
	printf("出度为0的顶点数为:%d\n",temp);
}

void InDs2(AlGraph AG)						//以邻接表的形式存储,找每个顶点的入度 
{
	ArcNode *p;
	for(int i=1;i<=AG.n;i++)
	{
		int nn2=0;
		for(int j=1;j<=AG.n;j++)
		{
			p=AG.adjlist[j].fristarc;
			while(p != NULL)
			{
				if(p->adjvex == i)
					nn2++;
				p = p->nextarc;
			}
		}
		printf("顶点%d的入度为:%d\n",i,nn2);
	}
	cout<<endl; 
}
void OutDs2(AlGraph AG)						//以邻接表的形式存储,找每个顶点的出度 
{
	ArcNode *p;
	for(int i=1;i<=AG.n;i++)
	{
		int nn1=0;
		p=AG.adjlist[i].fristarc;
		while(p != NULL)
		{
			nn1++;
			p = p->nextarc;
		}
		printf("顶点%d的出度为:%d\n",i,nn1);
	}
	cout<<endl; 
}
void ZeroOutDs2(AlGraph AG)					//以邻接表的形式存储,出度为 0的顶点个数 
{
	int temp=0;
	for(int i=1;i<=AG.n;i++)
	{
		if(AG.adjlist[i].fristarc == NULL)
			temp++;
	}
	printf("出度为0的顶点数为:%d\n",temp);
}

为了测试方便,我在贴上几个样例可供测验:

第一部分样例(无向图)
5 6
1 2 1
1 4 1
2 3 1
2 5 1
3 4 1
3 5 1

第一部分样例(有向图)
4 4
1 2 1
1 3 1
2 4 1
4 1 1

第二部分样例(无向图)
8
3 2 0
5 4 1 0
7 6 1 0
8 2 0
8 2 0
7 3 0
6 3 0
5 4 0

第二部分样例(有向图) 
4
2 3 0
4 0
0
1 0

后序还有各种关于图的各种功能的算法我会慢慢补充,要回宿舍了溜了溜了。

算了,临走之前把全部的代码贴上吧...

#include<iostream> 
#include<queue>
#include<cstdlib>
#define Status int 
#define INFINITY 32767			//定义最大值 
#define MAX_VERTEX_NUM 9		//定义最大节点为8+1,从1开始存储的 
using namespace std;
int b[MAX_VERTEX_NUM];
int d[MAX_VERTEX_NUM];
typedef char InfoType;  		//其他信息,可自定义 

//图的邻接矩阵存储形式 
typedef struct{
	int no;				//顶点标号(默认为1-n) 
	InfoType *info;		//存储顶点信息(比如顶点的名字)
}VertexType;

typedef struct{
	VertexType vex[MAX_VERTEX_NUM];				//顶点向量 
	int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];	//邻接矩阵数组,相邻就是1,不相邻就是0,也可以存储权值 
	int n,e;									//图的当前顶点数和弧数 
}MGraph;

//图的邻接表存储形式
typedef struct ArcNode{		//链表节点 
	int adjvex;				//该弧所指向的顶点的位置 
	int weight;				//网的权值 
	ArcNode *nextarc;		//指向下一条弧的指针 
}ArcNode; 

typedef struct{
	VertexType data;		//顶点信息:标号和其他信息 
	ArcNode *fristarc;		//第一个表节点的地址,指向第一条依附该顶点的弧的指针 
}VNode;

typedef struct{
	VNode adjlist[MAX_VERTEX_NUM];	//单链表的个数(顶点的个数) 
	int n,e;						//邻接表存储的图的当前顶点数和弧数
}AlGraph;

//正文 
queue<int>q;						//定义int型队列 

Status CreateMat(MGraph &G)			//创建一个邻接矩阵,只需输入点与点之间的关系即可 
{
	//char iofo1;
	int v1,v2,w,bh;
	cin>>G.n>>G.e;					//先数一数顶点数和边数 
	for(int i=1;i<=G.n;i++)
	{
		//cin>>bh;
		G.vex[i].no = i;			//给G.n个顶点标号为1-n,也可以自定义标号bh
		//cin>>info1;				//存储一些顶点信息,比如"青岛" 
		//G.vex[i].info = info1;
		for(int j=1;j<=G.n;j++)		//给矩阵赋初值0 
			G.arcs[i][j] = 0;
	}
	for(int i=1;i<=G.e;i++)		 
	{
		cin>>v1>>v2>>w;				//输入顶点向量和两个顶点向量之间的关系 
		G.arcs[v1][v2] = w;			//存储弧度信息,权值或者存1表示两顶点向量相邻 
		G.arcs[v2][v1] = w;
	}
}

Status CreateList(AlGraph &AGG)			//创建一个邻接表 
{
	int t,sum_e=0;
	ArcNode *p;
	cin>>AGG.n;							//输入邻接表的顶点数 
	for(int i=1;i<=AGG.n;i++)
	{
		AGG.adjlist[i].fristarc = NULL;	//给邻接表中的所有头结点的指针域赋初值 
		AGG.adjlist[i].data.no = i;
		cin>>t;
		while(t != 0)
		{
			p = (ArcNode *)malloc(sizeof(ArcNode));
			p->adjvex = t;
			p->nextarc = AGG.adjlist[i].fristarc;
			AGG.adjlist[i].fristarc = p;
			sum_e++;					//记录弧数的二倍,因为两点之间连接了两次 
			cin>>t;
		}
	}
	AGG.e = sum_e/2;
}

Status MatToList(MGraph G,AlGraph &AG)	//将邻接矩阵转为邻接表 
{
	ArcNode *p;
	AG.n = G.n;							//将邻接矩阵的顶点数和弧数赋给邻接表 
	AG.e = G.e;
	for(int i=1;i<=G.n;i++)
	{
		AG.adjlist[i].fristarc = NULL;			//给邻接表中的所有头结点的指针域赋初值 
		AG.adjlist[i].data.no = G.vex[i].no;	//将邻接矩阵的编号赋给邻接表的头节点 
	}
	for(int i=1;i<=G.n;i++)
	{
		for(int j=G.n;j>=1;j--)
		{
			if(G.arcs[i][j]!=0)			//如果两个点之间相连,就用头插法插入节点信息 
			{
				 p = (ArcNode *)malloc(sizeof(ArcNode));
				 p->adjvex = j;			//顶点的位置(几号) 
				 p->nextarc = AG.adjlist[i].fristarc;
				 AG.adjlist[i].fristarc = p;
			}
		}
	}
}

Status ListToMat(MGraph &GG,AlGraph AGG)	//将邻接表转为邻接矩阵 
{
	ArcNode *p;
	GG.n = AGG.n;
	GG.e = AGG.e;
	
	for(int i=1;i<=GG.n;i++)				//初始化邻接矩阵 
		for(int j=1;j<=GG.n;j++)
			GG.arcs[i][j] = 0;
	
	for(int i=1;i<=AGG.n;i++)				//将每一顶点相连的点创建为一个个的单链表 
	{
		p = AGG.adjlist[i].fristarc;
		while(p)
		{
			GG.arcs[i][p->adjvex] = 1;
			p = p->nextarc;
		}
	}
}

void DisMat(MGraph G)					//打印邻接矩阵 
{
	cout<<"该邻接矩阵为:"<<endl; 
	for(int i=1;i<=G.n;i++)
	{
		for(int j=1;j<=G.n;j++)
			cout<<G.arcs[i][j]<<" ";
		cout<<endl;
	}
}

void DisList(AlGraph AG)				//打印邻接表 
{
	cout<<"该邻接表为:"<<endl; 
	ArcNode *p;
	for(int i=1;i<=AG.n;i++)
	{
		p = AG.adjlist[i].fristarc;
		cout<<AG.adjlist[i].data.no;	//打印节点的标号 
		while(p)
		{
			cout<<"->"<<p->adjvex;
			p = p->nextarc;
		}
		cout<<endl;
	}
}

void Destory_List(AlGraph &AGG)		//销毁图的邻接表 
{
	ArcNode *p,*q;
	for(int i=1;i<=AGG.n;i++)
	{
		p = AGG.adjlist[i].fristarc;
		while(p)
		{
			q = p->nextarc;
			free(p);
			p = q;
		}
	}
}

//图的两种搜索方式:深度优先搜索DFS和广度优先搜索BFS 
void DFS(AlGraph AG,int v)					//深度优先搜索遍历邻接表 
{
	cout<<AG.adjlist[v].data.no<<"  ";		//先输出每个顶点的标号 
	d[v] = 1;								//已经遍历过的顶点标记为 1 
	ArcNode *p;
	p = AG.adjlist[v].fristarc;				//创建一个新节点怕,并指向编号为v的头结点 
	
	while(p != NULL)
	{
		if(!d[p->adjvex])					//如果该顶点没有被标记过,就该顶点的单链表 
			DFS(AG,p->adjvex);
		p=p->nextarc;
	}
}

void BFS(AlGraph AG,int v)					//广度优先搜索遍历邻接表 
{
	cout<<AG.adjlist[v].data.no<<"  ";		//先输出每个顶点的标号
	b[v] = 1;								//已经遍历过的顶点标记为 1
	q.push(v);
	ArcNode *p;
	p = AG.adjlist[v].fristarc;				//创建一个新节点,并指向编号为v的头结点
	
	while(p!=NULL)							//将顶点标号为v的单链表中的元素全部入队列
	{										//也就是将你想遍历的第一个点相邻的节点入队列 
		q.push(p->adjvex);
		p=p->nextarc;
	}
	while(!q.empty())						//只要队列不空,也就是还没有遍历完整个邻接表就不会停止 
	{
		p=AG.adjlist[q.front()].fristarc;	//让p指向队列第一个元素指向的顶点 
		while(p)							//将与顶点p相连的顶点全部入队列 
		{	
			if(!b[p->adjvex])				//如果该顶点没有遍历过 
			{
				q.push(p->adjvex);			//将该顶点入队列 
				cout<<AG.adjlist[p->adjvex].data.no<<"  ";	//输出顶点的值 
				b[p->adjvex]=1;								//并标记为 1 
			}
			p=p->nextarc; 
		}
		q.pop();							//将与p相连的所有节点遍历完并入队列后,将该顶点出队列 
	}
	cout<<endl;
}

void InDs(MGraph G)							//以邻接矩阵的形式存储,找每个顶点的入度 
{
	for(int i=1;i<=G.n;i++)
	{
		int sum=0;
		for(int j=1;j<=G.n;j++)
		{
			if(G.arcs[j][i] != 0)
				sum++;
		}
		printf("顶点%d的入度为:%d\n",i,sum);
	}
	cout<<endl; 
}
void OutDs(MGraph G)						//以邻接矩阵的形式存储,找每个顶点的出度 
{
	for(int i=1;i<=G.n;i++)
	{
		int sum=0;
		for(int j=1;j<=G.n;j++)
		{
			if(G.arcs[i][j] != 0)
				sum++;
		}
		printf("顶点%d的出度为:%d\n",i,sum);
	}
	cout<<endl; 
}
void ZeroOutDs(MGraph G)					//以邻接矩阵的形式存储,出度为 0的点的个数 
{
	int temp=0;
	for(int i=1;i<=G.n;i++)
	{
		int sum=0;
		for(int j=1;j<=G.n;j++)
		{
			if(G.arcs[i][j] != 0)
				sum++;
		}
		if(sum == 0)
			temp++;
	}
	printf("出度为0的顶点数为:%d\n",temp);
}

void InDs2(AlGraph AG)						//以邻接表的形式存储,找每个顶点的入度 
{
	ArcNode *p;
	for(int i=1;i<=AG.n;i++)
	{
		int nn2=0;
		for(int j=1;j<=AG.n;j++)
		{
			p=AG.adjlist[j].fristarc;
			while(p != NULL)
			{
				if(p->adjvex == i)
					nn2++;
				p = p->nextarc;
			}
		}
		printf("顶点%d的入度为:%d\n",i,nn2);
	}
	cout<<endl; 
}
void OutDs2(AlGraph AG)						//以邻接表的形式存储,找每个顶点的出度 
{
	ArcNode *p;
	for(int i=1;i<=AG.n;i++)
	{
		int nn1=0;
		p=AG.adjlist[i].fristarc;
		while(p != NULL)
		{
			nn1++;
			p = p->nextarc;
		}
		printf("顶点%d的出度为:%d\n",i,nn1);
	}
	cout<<endl; 
}
void ZeroOutDs2(AlGraph AG)					//以邻接表的形式存储,出度为 0的顶点个数 
{
	int temp=0;
	for(int i=1;i<=AG.n;i++)
	{
		if(AG.adjlist[i].fristarc == NULL)
			temp++;
	}
	printf("出度为0的顶点数为:%d\n",temp);
}


int main()
{
	//第一部分是邻接矩阵转为邻接表,输入的是每两个顶点之间的关系(相邻与否)或者权值 
	MGraph G;
	AlGraph AG;
	CreateMat(G);
	DisMat(G);
	MatToList(G,AG);
	DisList(AG);
	
	InDs(G);
	OutDs(G);
	ZeroOutDs(G);
	cout<<endl;
	InDs2(AG);
	OutDs2(AG);
	ZeroOutDs2(AG);
	
	Destory_List(AG);
	//第二部分是邻接表转为邻接矩阵,输入的是和每个顶点相连的顶点,每个顶点的输入以结束
	/*MGraph GG;
	AlGraph AGG;
	CreateList(AGG);
	DisList(AGG);
	ListToMat(GG,AGG);
	DisMat(GG);
	
	DFS(AGG,5);
	cout<<endl;
	BFS(AGG,5);
	Destory_List(AGG);*/

	return 0;
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值