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

来源:互联网 发布:淘宝联盟用了红包丢单 编辑:程序博客网 时间:2024/06/01 09:04

本博客记录的是有关图的学习与总结,主要涉及以下几个方面: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<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;//先将邻接矩阵的元素全部置0for (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;}


1 0
原创粉丝点击