[C++]图的邻接矩阵、邻接表及其相互转化和邻接表的广度遍历、深度遍历

本博客记录的是有关图的学习与总结,主要涉及以下几个方面:1.图的邻接矩阵和邻接表的数据结构表示;2.邻接矩阵和邻接表的相互转化;3.邻接表的深度遍历和广度遍历。

一、图的邻接矩阵和邻接表

  A.基本概念  

  图的存储结构主要分两种,一种是邻接矩阵,一种是邻接表。 
1.邻接矩阵 
  图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息。 
设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为: 
《[C++]图的邻接矩阵、邻接表及其相互转化和邻接表的广度遍历、深度遍历》 
  看一个实例,下图左就是一个无向图。 
《[C++]图的邻接矩阵、邻接表及其相互转化和邻接表的广度遍历、深度遍历》 
  从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij = aji。即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的。 
  从这个矩阵中,很容易知道图中的信息。 
(1)要判断任意两顶点是否有边无边就很容易了; 
(2)要知道某个顶点的度,其实就是这个顶点vi在邻接矩阵中第i行或(第i列)的元素之和; 
(3)求顶点vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点; 
  而有向图讲究入度和出度,顶点vi的入度为1,正好是第i列各数之和。顶点vi的出度为2,即第i行的各数之和。 
  若图G是网图,有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:

《[C++]图的邻接矩阵、邻接表及其相互转化和邻接表的广度遍历、深度遍历》

1.2 邻接表 
  邻接矩阵是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费。因此,找到一种数组与链表相结合的存储方法称为邻接表。 
  邻接表的处理方法是这样的: 
(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。 
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。 
  例如,下图就是一个无向图的邻接表的结构。 
《[C++]图的邻接矩阵、邻接表及其相互转化和邻接表的广度遍历、深度遍历》 
  从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。 
  对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。如下图所示。 
《[C++]图的邻接矩阵、邻接表及其相互转化和邻接表的广度遍历、深度遍历》 
3.两者区别 
  对于一个具有n个顶点e条边的无向图,它的邻接表表示有n个顶点表结点2e个边表结点 
  对于一个具有n个顶点e条边的有向图,它的邻接表表示有n个顶点表结点e个边表结点 
  如果图中边的数目远远小于n2称作稀疏图,这是用邻接表表示比用邻接矩阵表示节省空间; 
  如果图中边的数目接近于n2,对于无向图接近于n*(n-1)称作稠密图,考虑到邻接表中要附加链域,采用邻接矩阵表示法为宜。

  B.数据结构的设计

  1.邻接矩阵

  包含一个二维数组来记录邻接矩阵,然后创建两个int来记录顶点个数和边的条数,也可以选择性地创建一维数组记录顶点信息。

如下:

typedef struct{
	int edges[MAXVEX][MAXVEX];//邻接矩阵
	int numNodes, numEdges;
	ElemType vex[MAXVEX];//存放顶点信息
}GraphAdjArray;

  2.邻接表

  包括顶点表和边表

顶点表包括顶点和下一个邻接点的指针

边表包括存储顶点的序号,和指向下一个的值

//边表结点
typedef struct EdgeNode
{
 int adjvex;
 EdgeType weight;
 struct EdgeNode *next;
}EdgeNode;
//顶点表结点
typedef struct VextexNode {
 VertexType data;
 bool visited;//表示该节点是否被访问
 EdgeNode *firstedge;
}VetexNode,AdjList[MAXVEX];
//邻接表
typedef struct
{
 AdjList adjList;
 int numNodes, numEdges;
}GraphAdjList;

  

  C.创建邻接矩阵和邻接表

1.创建邻接矩阵

  如果传入的直接是矩阵形式,那么设置两个循环,然后逐一将元素赋值到二维数组中即可

  如果传入的形式是 边_begin,边_end,权重。这种数组形式,先创建邻接矩阵全部元素置为0,然后遍历输入的数组,将边的i和j传入对应的邻接矩阵中。

2.创建邻接表

  只考虑传入的形式是 边_begin,边_end,权重。(单链表的头插法)《[C++]图的邻接矩阵、邻接表及其相互转化和邻接表的广度遍历、深度遍历》

注意如果是无向图,应该重复上面的操作,然后1,2顺序调换。

D.邻接矩阵和邻接表的相互转化

   其实问题可以转化为如何遍历邻接矩阵和邻接表的问题

1.邻接矩阵->邻接表

等价于遍历邻接矩阵问题+构建邻接表问题

2.邻接表->邻接矩阵

等价于遍历邻接表问题。遍历邻接表的头结点,然后在每个头结点中遍历边表结点。

二、广度优先遍历和深度优先遍历

参考:http://blog.csdn.net/zhangxiangdavaid/article/details/38323633

 

下面是上述内容的所有参考代码:

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

const int MAXVEX = 100;
typedef char VertexType;
typedef int EdgeType;
//邻接矩阵
typedef struct
{
	int num;
	EdgeType weight;
}ElemType;	//矩阵中元素的类型

typedef struct{
	int edges[MAXVEX][MAXVEX];//邻接矩阵
	int numNodes, numEdges;
	ElemType vex[MAXVEX];//存放顶点信息
}GraphAdjArray;

//邻接表
//边表结点
typedef struct EdgeNode
{
	int adjvex;
	EdgeType weight;
	struct EdgeNode *next;
}EdgeNode;
//顶点表结点
typedef struct VextexNode {
	VertexType data;
	bool visited;//表示该节点是否被访问
	EdgeNode *firstedge;
}VetexNode,AdjList[MAXVEX];
//邻接表
typedef struct
{
	AdjList adjList;
	int numNodes, numEdges;
}GraphAdjList;

//创建邻接列表
//传入 边界点begin 边界点end 权重
void CreateALGraph(GraphAdjList *Gp, int arr[][3],int numNodes, int numEdges)
{
	int i;
	EdgeNode *pe;
	Gp->numNodes = numNodes;
	Gp->numEdges = numEdges;
	//输入顶点信息,将边表置为0 
	for (i = 0;i < Gp->numNodes;++i) {
		Gp->adjList[i].data = i;
		Gp->adjList[i].firstedge = NULL;
	}
	//建立边表
	for (i = 0;i < Gp->numEdges;++i) {
		pe = (EdgeNode*)malloc(sizeof(EdgeNode));
		pe->adjvex = arr[i][1];
		pe->weight = arr[i][2];
		pe->next = Gp->adjList[arr[i][0]].firstedge;
		Gp->adjList[arr[i][0]].firstedge = pe;
		//重复上面的操作
		pe = (EdgeNode*)malloc(sizeof(EdgeNode));
		pe->adjvex = arr[i][0];
		pe->weight = arr[i][2];
		pe->next = Gp->adjList[arr[i][1]].firstedge;
		Gp->adjList[arr[i][1]].firstedge = pe;
	}
}

//创建邻接矩阵
void CreateALArray(GraphAdjArray *Ga, int arr[][18], int numNodes, int numEdges)
{
	Ga->numEdges = numEdges;
	Ga->numNodes = numNodes;
	int i, j;
	//直接将邻接矩阵写到edges中,没有用到ElemType vex[MAXVEX]
	for (i = 0;i < Ga->numNodes;++i) {
		for (j = 0;j < Ga->numNodes;++j)
			Ga->edges[i][j] = arr[i][j];
	}
}

//输出邻接表
void DispAdjList(GraphAdjList *Gp) {
	int i;
	EdgeNode *p;
	for (i = 0;i < Gp->numNodes;++i) {
		p = Gp->adjList[i].firstedge;
		cout << i << ": ";
		while (p != NULL) {
			cout << p->adjvex << " ";
			p = p->next;
		}
		cout << endl;
	}
}

//输出邻接矩阵
void DispAdjArray(GraphAdjArray *Ga) {
	int i, j;
	for (i = 0;i < Ga->numNodes;++i) {
		for (j = 0;j < Ga->numNodes;++j) {
			cout << Ga->edges[i][j] << " ";
		}
		cout << endl;
	}
}

//邻接矩阵转化成邻接表
void ArratToList(GraphAdjArray *Ga, GraphAdjList *GatoGp) {
	GatoGp->numEdges = Ga->numEdges;
	GatoGp->numNodes = Ga->numNodes;
	//输入顶点信息,将边表置为0 
	int i,j;
	for (i = 0;i < GatoGp->numNodes;++i) {
		GatoGp->adjList[i].data = i;
		GatoGp->adjList[i].firstedge = NULL;
	}
	EdgeNode *pe;
	pe = (EdgeNode*)malloc(sizeof(EdgeNode));
	for (i = 0;i < Ga->numNodes;++i) {
		for (j = 0;j < Ga->numNodes;++j) {
			if (Ga->edges[i][j] != 0) {
				pe = (EdgeNode*)malloc(sizeof(EdgeNode));
				pe->adjvex = j;
				pe->weight = Ga->edges[i][j];
				pe->next = GatoGp->adjList[i].firstedge;
				GatoGp->adjList[i].firstedge = pe;
			}
		}
	}

}
//邻接表转化成邻接矩阵
void ListToArray(GraphAdjList *Gp, GraphAdjArray *GptoGa) {
	EdgeNode *pe;
	GptoGa->numEdges = Gp->numEdges;
	GptoGa->numNodes = Gp->numNodes;
	int i, j;
	//先将邻接矩阵的元素全部置0
	for (i = 0;i < GptoGa->numNodes;++i) {
		for (j = 0;j < GptoGa->numNodes;++j)
			GptoGa->edges[i][j] = 0;
	}
	for (i = 0;i < Gp->numNodes;++i) {
		pe = Gp->adjList[i].firstedge;
		while (pe != NULL) {
			GptoGa->edges[i][pe->adjvex] = pe->weight;
			pe = pe->next;
		}
	}
}
//初始化节点的访问
void initVisted(GraphAdjList *Gp) {
	for (int i = 0;i < Gp->numNodes;++i) {
		Gp->adjList[i].visited = 0;
	}
}

//邻接表的深度搜索遍历,将结果保存到vector中
void DFS(GraphAdjList *Gp,int start,vector<int> *vec) {
	vec->push_back(start);
	cout << start << " ";
	Gp->adjList[start].visited = 1;
	EdgeNode *pe;
	pe = (EdgeNode*)malloc(sizeof(EdgeNode));
	pe = Gp->adjList[start].firstedge;	//pe指向表头
	while (pe != NULL) {				//遍历Gp->adjList[start]的边表,找到未遍历的
		if (!Gp->adjList[pe->adjvex].visited) {
			DFS(Gp, pe->adjvex,vec);
		}
		pe = pe->next;
	}
}
//邻接表的广度搜索
void BFS(GraphAdjList *Gp, int start, vector<int> *vec) {
	queue<int> q;
	q.push(start);
	Gp->adjList[start].visited = 1;
	while (!q.empty()) {
		int current = q.front();
		q.pop();
		vec->push_back(current);
		EdgeNode *pe;
		pe = (EdgeNode*)malloc(sizeof(EdgeNode));
		pe = Gp->adjList[current].firstedge;//pe指向头结点,遍历边表
		while ((pe !=NULL )&& (!Gp->adjList[pe->adjvex].visited)) {
			q.push(pe->adjvex);
			Gp->adjList[pe->adjvex].visited = 1;
			pe = pe->next;
		}
	}
}
int main(){
	GraphAdjList Gp;
	int arr1[][3] = { {1,0,3},
	{2,0,1},
	{3,0,1},
	{2,1,1},
	{4,1,1},
	{9,1,4},
	{3,2,1},
	{4,2,2},
	{5,2,1},
	{5,3,2},
	{6,3,2},
	{7,3,1},
	{5,4,1},
	{9,4,1},
	{6,5,1},
	{9,5,3},
	{10,5,1},
	{12,5,3},
	{7,6,1},
	{8,6,2},
	{12,6,2},
	{13,6,4},
	{14,6,3},
	{8,7,1},
	{14,8,1},
	{15,8,3},
	{10,9,1},
	{11,9,1},
	{11,10,1},
	{12,10,2},
	{16,11,1},
	{13,12,2},
	{16,12,1},
	{14,13,1},
	{15,13,2},
	{16,13,2},
	{17,13,1},
	{15,14,1},
	{17,15,4},
	{17,16,1 }};
	CreateALGraph(&Gp, arr1,18, 40);
	cout << "***********Gp**************" << endl;
	DispAdjList(&Gp);

	//输出邻接表
	GraphAdjArray Ga;
	int arr2[18][18] = { { 0,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
	{ 3,0,1,0,1,0,0,0,0,4,0,0,0,0,0,0,0,0 },
	{ 1,1,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0 },
	{ 1,0,1,0,0,2,2,1,0,0,0,0,0,0,0,0,0,0 },
	{ 0,1,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0 },
	{ 0,0,1,2,1,0,1,0,0,3,1,0,3,0,0,0,0,0 },
	{ 0,0,0,2,0,1,0,1,2,0,0,0,2,4,3,0,0,0 },
	{ 0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0 },
	{ 0,0,0,0,0,0,2,1,0,0,0,0,0,0,1,3,0,0 },
	{ 0,4,0,0,1,3,0,0,0,0,1,1,0,0,0,0,0,0 },
	{ 0,0,0,0,0,1,0,0,0,1,0,1,2,0,0,0,0,0 },
	{ 0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0 },
	{ 0,0,0,0,0,3,2,0,0,0,2,0,0,2,0,0,1,0 },
	{ 0,0,0,0,0,0,4,0,0,0,0,0,2,0,1,2,2,1 },
	{ 0,0,0,0,0,0,3,0,1,0,0,0,0,1,0,1,0,0 },
	{ 0,0,0,0,0,0,0,0,3,0,0,0,0,2,1,0,0,4 },
	{ 0,0,0,0,0,0,0,0,0,0,0,1,1,2,0,0,0,1 },
	{ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,4,1,0 }};
	CreateALArray(&Ga, arr2, 18, 40);
	DispAdjArray(&Ga);
	GraphAdjList GatoGp;
	ArratToList( &Ga, &GatoGp);
	cout << "********邻接数组转化为邻接表的结果***********" << endl;
	DispAdjList(&GatoGp);
	GraphAdjArray GptoGa;
	ListToArray(&GatoGp, &GptoGa);
	cout << "********邻接表转化为邻接矩阵的结果***********" << endl;
	DispAdjArray(&GptoGa);

	//根据Gp学习深度遍历
	vector<int> vec1 = {};
	initVisted(&Gp);
	cout << "输出深度遍历的结果" << endl;
	DFS(&Gp, 0, &vec1);
	//根据Gp学习广度遍历
	cout << "输出广度遍历的结果" << endl;
	vector<int> vec2 = {};
	initVisted(&Gp);
	BFS(&Gp, 0, &vec2);
	system("pause");
	return 0;
}

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/smuevian/article/details/71598177
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞