本博客记录的是有关图的学习与总结,主要涉及以下几个方面:1.图的邻接矩阵和邻接表的数据结构表示;2.邻接矩阵和邻接表的相互转化;3.邻接表的深度遍历和广度遍历。
一、图的邻接矩阵和邻接表
A.基本概念
图的存储结构主要分两种,一种是邻接矩阵,一种是邻接表。
1.邻接矩阵
图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息。
设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:
看一个实例,下图左就是一个无向图。
从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是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的方阵,定义为:
1.2 邻接表
邻接矩阵是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费。因此,找到一种数组与链表相结合的存储方法称为邻接表。
邻接表的处理方法是这样的:
(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。
例如,下图就是一个无向图的邻接表的结构。
从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。如下图所示。
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,权重。(单链表的头插法)
注意如果是无向图,应该重复上面的操作,然后1,2顺序调换。
D.邻接矩阵和邻接表的相互转化
其实问题可以转化为如何遍历邻接矩阵和邻接表的问题
1.邻接矩阵->邻接表
等价于遍历邻接矩阵问题+构建邻接表问题
2.邻接表->邻接矩阵
等价于遍历邻接表问题。遍历邻接表的头结点,然后在每个头结点中遍历边表结点。
二、广度优先遍历和深度优先遍历
参考:http://blog.csdn.net/zhangxiangdavaid/article/details/38323633
下面是上述内容的所有参考代码:
#include
#include
#include
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 *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 *vec) {
queue 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 vec1 = {};
initVisted(&Gp);
cout << "输出深度遍历的结果" << endl;
DFS(&Gp, 0, &vec1);
//根据Gp学习广度遍历
cout << "输出广度遍历的结果" << endl;
vector vec2 = {};
initVisted(&Gp);
BFS(&Gp, 0, &vec2);
system("pause");
return 0;
}