数据结构学习笔记(19) ---图的存储与遍历

来源:互联网 发布:知乎提问没人浏览 编辑:程序博客网 时间:2024/05/21 09:29

(一)图的基本术语

(1)无向完全图:图中任意两个顶点都有边存在则该图为无向完全图,且图的边树数为:n*(n-1)/2
(2)有向完全图:图中任意两点都有方向相反的边则该图为有向完全图,且图的边树数为:n*(n-1)
(3)连通图:若力图中任意两个顶点都有路径(即可到达)则成该图为连通的。
(4)连通分量:无向图中的极大连通子图称为该图的连通分量,显然无向连通图的连通分量为其自身。
(5)强连通图:无向图中任意两点都有路径则称该有向图为强连通图。
(6)强连通分量:有向图中的极大连通子图称为强连通分量。
例如:

(二)图的存储结构

(1)邻接矩阵

无权值:定义一个数组,图中两个顶点在数组中有一个对应的位置,其他为零
有权值:定义一个数组,图中两个顶点在图中有一个对应的位置放权值,其他为无穷大.
例如:


代码实现:

#define MAXSIZE 20//图的存储 (邻接矩阵)typedef struct Node{    int e;//边的个数    int n;//顶点的个数    int vertex[MAXSIZE];    int edge[MAXSIZE][MAXSIZE];}Graph;void CreateGraph(Graph * g){    cout << "请输入顶点个数:";    cin >> g->n;    cout << "请输入边的个数:";    cin >> g->e;    for (size_t i = 0; i < g->n; i++)    {        g->vertex[i] = i;    }    //初始化邻接矩阵    for (size_t i = 0; i < g->n; i++)    {        for (size_t j = 0; j < g->n; j++)        {            g->edge[i][j] = 0;        }    }    for (size_t i = 0; i < g->n; i++)    {        int x, y;        cout << "请输入边信息:";        cin >> x >> y;        g->edge[x][y] = 1;        g->edge[y][x] = 1;    }}
(1)邻接表:定义一个含有全部顶的点结构体数组,数组中存放节点值与一个链表的头指针,该头指针指向的链表存放与该节点相邻的顶点。假如再存放有权值则添加一个属性就可以。

例如:

代码实现:

//图的存储(邻接表)typedef struct node{    int adjvex;//邻接点    struct node *next;//指向下一个邻接点}EdgeNode;typedef struct arr{    int vertex;//顶点域    EdgeNode *first;//邻接表第一个指针域}VertexNode;void CreateListGraph(VertexNode *g,int n,int e){    //初始化邻接矩阵    for (size_t i = 0; i < n; i++)    {        g[i].vertex = i;        g[i].first = NULL;    }    for (size_t i = 0; i < e; i++)    {        cout << "请输入顶点的信息:";        int x, y;        cin >> x >> y;        EdgeNode *pNew = new EdgeNode;        pNew->adjvex = y;        pNew->next = g[x].first;        g[x].first = pNew;        pNew = new EdgeNode;        pNew->adjvex = x;        pNew->next = g[y].first;        g[y].first = pNew;    }}

(三)图的遍历

(1)深度优先遍历
算法思想:1)访问顶点v;2)从v的未被访问的邻接点出发,继续对图进行深度优先遍历,若从某点出发所有邻接点都已访问过,退回前一个点继续上述过程,若退回开始点,结束。  

例如:

按照上面图进行遍历顺序如下:

**所以遍历的一种序列: V1 , V2 , V4 , V8 , V5 ,
V3 , V6 , V7 , V9 , V10 , V12 , V11 **
注意:便利序列是和你的存储方式有关的,如果使用邻接矩阵 则:
代码如下:

int visit[MAXSIZE];//标记顶点是否访问void DFSGraph(Graph* g, int n){    cout << g->vertex[n] << " ";    visit[n] = 1;//若访问过就标记为1    for (size_t i = 0; i < g->n; i++)    {        if ( !visit[i]&& g->edge[n][i] == 1 )        {            DFS(g, i);        }    }}void DFSTraveseGraph(Graph *g){    for (size_t i = 0; i < g->n; i++)    {        visit[i] = 0;    }    for (size_t i = 0; i < g->n; i++)    {        if (!visit[i])        {            DFS(g, i);//防止连通图        }    }}

如果使用邻接表 则: 代码如下:

//图的深度优先搜索遍历(邻接表)int visit[MAXSIZE];//标记顶点是否访问void DFS(VertexNode* g,int i){    cout << g[i].vertex << " ";    visit[i] = 1;//若访问过就标记为1    EdgeNode *p = g[i].first;//找到其下一个连接点    while (p!=NULL)    {        if (!visit[p->adjvex])//没有被访问则继续深度搜索        {            DFS(g, p->adjvex);        }        p = p->next;    }}void DFSTravese(VertexNode *g,int n){    for (size_t i = 0; i < n; i++)    {        visit[i] = 0;    }    for (size_t i = 0; i < n; i++)    {        if(!visit[i])        {            DFS(g, i);//防止连通图        }    }}
(2)广度优先遍历
算法思想:(1)首先访问顶点v(2)访问顶点v的相邻节点 (3)然后依次从邻接点出发再访问其相邻节点其实就是相当于树的层次遍历

在此只写出基于邻接表存储的图的广度优先遍历代码如下:

//广度优先搜索#include <queue>void BFS(VertexNode *g,int n){    cout << g[n].vertex<<" ";//输出第一个邻接点    visit[n] = 1;    queue<int> q;//定义一个队列用来存放顶点    q.push(n);//首先把顶点n入队    while (!q.empty())//判断队列是否为空    {        int i = q.back();//取队顶点        q.pop();        EdgeNode *p = g[i].first;        while ( p )        {            if (!visit[p->adjvex])            {                cout << p->adjvex << " ";                visit[p->adjvex] = 1;                q.push(p->adjvex);            }            p = p->next;        }    }}

好了终于写完了,累死我了,基于树的存储方式还有很多种,但是比较麻烦,也是比较少用,也就没有介绍。有意向可以自己研究。

原创粉丝点击