数据结构知识整理 - 遍历图 - 深度优先搜索

本文详细介绍了图的深度优先搜索遍历方法,包括以邻接矩阵和邻接表为存储结构的遍历过程,阐述了DFS在遍历过程中如何避免重复访问,并分析了不同存储结构下的时间复杂度。

主要内容


 

图的遍历算法是实现图的其他算法的基础,图的遍历方式有两种:深度优先搜索遍历广度优先搜索遍历

深度优先搜索遍历类似于树的先序遍历(根节点→左子树→右子树),借助了结构以实现递归广度优先搜索遍历则类似于层次遍历,借助于队列结构实现。

因为图的任意两个顶点都有可能存在联系,所以在访问某个顶点后,可能沿着某条路径又会回到访问过的顶点。为了避免同一顶点被访问多次,在遍历图的过程中,必须用一个数组visited[vexnum]记录每个访问过的顶点。

邻接矩阵为存储结构的遍历,时间复杂度为O(n²);以邻接表为存储结构的遍历,时间复杂度为O(n+e)

 

 

深度优先搜索

深度优先搜索(Depth First Search,DFS)遍历是树的先序遍历的推广。

 

遍历过程:

(1)从图中的某个顶点开始访问;

(2)若顶点存在未被访问的邻接顶点,则继续访问它的其中一个邻接顶点;

(3)重复步骤(2)直至某个顶点不存在未被访问的邻接顶点;

(4)从步骤(3)的某个顶点开始回溯访问,若回溯访问的邻接顶点存在未被访问的邻接顶点,则再执行步骤(2)(3)(4);

(5)重复步骤(2)(3)(4)直至所有顶点均被遍历,NOT EXISTS visted[n] 的值为0。

 

这种一条路走到底,走不通再返回的特征正是“深度”一词的体现。

需要提一下的是,深度优先算法的代码并没有体现“回溯”的过程,“回溯”体现在标记数组visited[vexnum]中,遍历图的所有顶点一个深入、判断、覆盖的过程。

 

 

邻接矩阵为存储结构的深度优先搜索遍历

​
#define TRUE 1                          /*已访问*/
#define False 0                         /*未访问*/

void DFS_AMG(AMGraph &G, int v1)         /*v1为起始顶点*/
{
    int visited[G.vexnum] = {0};        /*定义记录已访问顶点的数组*/
                                        /*定义局部数组时,只将首元素初始化为0,其余元素自动设0*/
    cout<<v1;                           /*以输出语句表示遍历,对顶点进行其他操作则换为函数调用*/
    int i = LocateVex(G, v1);           /*确定顶点v1对应的编号*/
    visited[i] = TRUE;                  /*记录已访问顶点*/

    for(int v2 = 1; v2 <= G.vexnum && v2 != v1; v2++)       /*v2图中的另一个顶点*/
    {
        int j = LocateVex(G, v2);                           /*同样需要确定v2的编号*/

        /*当v1与v2有关联且v2未被访问,递归执行DFS_AMG()*/
        if(G.arcs[i][j] != 0 && visited[j] = False)              
            DFS_AMG(G,v2);
    }
}

​

理解递归过程:

(1)邻接矩阵中确定起始顶点v1所代表的第i行

(2)第i行中找到第一个值不为0的结点,该结点在第j列

(3)判断visited[j]的值是否为FALSE,若是,将注意力转到第j行

(4)在第j行中进行与步骤(1)(2)(3)相同的过程;

(5)若第i行中还存在第二个值不为0的结点,该结点在第k列,则在执行完所有从第i行、第j列(arcs[i][j])开始的过程后,再执行从arcs[i][k]开始的所有过程,始终按照“先被访问的顶点的邻接点,先于后被访问的顶点的邻接点”的原则;

(6)在步骤(5)中的顺序执行同样发生在每一个顶点上,即每一行中,所以深度优先遍历是一个深入、判断、覆盖(不断将visited[n]的值填为TRUE)的过程

 

提前路过的圈毛君:“虽然已经很努力去解释了,可自己看还是感觉有点拗口,希望大家能看明白叭_(:з」∠)_”

 

 

邻接表为存储结构的深度优先搜索遍历

​
void DFS_ALG(ALGraph &G, int v1)
{
    int visited[G.vexnum] = {0};    

    cout<<v1;
    visited[v1-1] = TRUE;               /*第i个顶点vi的编号为i-1*/
                                        /*在邻接矩阵中还需要vi的合理性,所以用LocateVex()函数来求编号*/

    ArcNode *p = G.ALs[v1-1].nextarcnode; /*指针p指向头结点v1指向的下一个边结点*/

    while(p != NULL)                    /*若边结点不为空*/
    {
        int v2 = p->anothervex;         /*通过边结点确定v1的一个邻接顶点*/

        if(visited[v2-1] = FALSE)
            DFS_ALG(G,v2);
        
        p = p->nextarcnode;             /*应用链式结构就不需要像邻接矩阵那样在循环语句中一列一列判断,给指针赋值为下一个指针就好*/
    }
}

​

理解代码的思路跟上面类似。

 

由深度优先的遍历过程可以得到一棵以起始顶点v1为根结点的树,这棵树称作深度优先生成树

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值