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

(一)图的基本术语

(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;
        }
    }
}

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值