Depth First Search ---- 深度优先遍历(DFS)

                     Depth First Search ---- 深度优先遍历

1.相关基础:

1.1 图的存储结构

         基于二维数组的邻接矩阵表示 +  基于链表的的邻接表。

说明: 
        如下为无向图的邻接矩阵的表示,V[ i ][ j ] = 1 表示连接;V[ i ][ j ] = 0表示不连接;

               这里写图片描述     这里写图片描述

1.2 图的遍历

     从图的某个顶点出发,访问图中的所有顶点,且使每个顶点仅被访问一次。

 

---------------------------------------------------------------------------------------------------------------------------

2.深度优先算法

2.1 思想

G(V , E)是一个有向图或者无向图1

做标记:开始时,图 G 的所有顶点都未曾被访问过;设置 visit [ n ] = 0 ; 当顶点被访问过后,设置参数变成1

选择出发点:从 G 中选取任意一点 u 作为出发点;

流程:

   1)从初始点(出发点)开始,把其标注为已经访问后,依次搜索 u 的每一个邻接顶点 v(若 v2 和 v3 都是其邻接节点,则可规定先从数字小的顶点开始,即 v2 )

   2)如果该顶点 v 未被访问过,则从 v 开始重复进行第一步的动作 , 以此进行深度优先遍历

   3)如果 u 的所有邻接顶点 v 均被访问过,如果图中所有节点全部被访问过一次,则遍历结束;如果图中仍有未被访问过的节点,则任意选取一个重新开始步骤1。

 

2.2 流程&例子

   如图为一个无向连接图:深度遍历依次访问的节点为:1 -- 2 -- 4 -- 8  -- 5 -- 3 -- 6 -- 7       

                               

(如下的流程描述使用的是数组。但是也可以使用其他的数据结构比如栈)

流程:

 1)首先选取 V1 为初始节点 ;输出  V1,标记 V1 的flag=true; 
 2)获得 V1 的邻接边 [ V2 V3 ], 取出 V2,标记 V2 的flag=true; (此处安装数字小的先遍历的原则,即2,3,先遍历2)
 3)获得 V2 的邻接边[ V1 V4 V5 ], 过滤掉已经 flag 的,取出 V4,标记 V4 的flag=true;
 4)获得 V4 的邻接边[ V2 V8 ], 过滤掉已经 flag的,取出 V8,标记 V8 的flag=true; 
 5)获得 V8 的邻接边[ V4 V5 ], 过滤掉已经 flag的,取出 V5,标记 V5 的flag=true; 
 6) 此时发现 V5 的所有邻接边都已经被flag了,所以需要回溯。(左边黑色虚线,回溯到 V1,回溯就是下层递归结束往回返) 

               

  特殊说明:在这里为了区分已经访问过的节点和没有访问过的节点,灰色的节点表示已经访问过了。原来图中 V2 只与上面的 V1 连接, 此处为什么在 V2 处又添加了一个 V1 就是为了说明,找的是每个节点的邻接节点,然后对找到的节点进行筛选。 

7) 回溯到 V1,在前面取出的是 V2,现在取出 V3,标记 V3 的flag=true; 

8) 获得 V3 的邻接边[ V1 V6 V7 ],过滤掉已经 flag 的, 取出 V6,标记 V6 的flag=true; 

9)获得V6的邻接边[V3 V7],过滤掉已经flag的,取出 V7,标记V7的flag=true;  

10)此时发现 V7 的所有邻接边都已经被 flag了,所以需要回溯。(右边黑色虚线,回溯到 V1,回溯就是下层递归结束往回返)

                                  

 

2.3 编码

使用数组的回溯法进行实现:

bool visited[MaxVnum];
void DFS(Graph G,int v)
{
    visited[v]= true; //从V开始访问,flag它
    printf("%d",v);    //打印出V
    for(int j=0;j<G.vexnum;j++) 
        if(G.arcs[v][j]==1 && visited[j]== false) //这里可以获得V未访问过的邻接点
            DFS(G,j); //递归调用,如果所有节点都被访问过,就回溯,而不再调用这里的DFS
}

void DFSTraverse(Graph G) {
    for (int v = 0; v < G.vexnum; v++)
        visited[v] = false; //刚开始都没有被访问过

    for (int v = 0; v < G.vexnum; ++v)
        if (visited[v] == false) //从没有访问过的第一个元素来遍历图
            DFS(G, v);
}

数据结构的定义:

#define MaxVnum 50
typedef double AdjMatrix[MaxVnum][MaxVnum];   //表示一个矩阵,用来存储顶点和边连接关系
typedef struct {
    int vexnum,arcnum;               //顶点的个数,边的个数
    AdjMatrix arcs;                 //图的邻接矩阵
}Graph;

图的初始化:

void CreateGraph(Graph &G)
{
    G.vexnum=8;
    G.arcnum=9;
    G.arcs[0][1]=1;
    G.arcs[0][2]=1;
    G.arcs[1][3]=1;
    G.arcs[1][4]=1;
    G.arcs[2][5]=1;
    G.arcs[2][6]=1;
    G.arcs[3][1]=1;
    G.arcs[3][7]=1;
    G.arcs[3][6]=1;
    G.arcs[4][1]=1;
    G.arcs[4][7]=1;
    G.arcs[5][2]=1;
    G.arcs[5][6]=1;
    G.arcs[5][5]=1;
    G.arcs[6][2]=1;
    G.arcs[6][5]=1;
    G.arcs[7][3]=1;
    G.arcs[7][4]=1;
}

2.4 使用栈来实现

当使用栈来存储的时候,我们仍需要一个数组 visit [ n ] 来判断节点是否曾经进入过栈中(也就是上面说的是否被遍历过的意思)。 算法的流程与使用数组的时候几乎相同,但是要注意使用数组的时候,如果有 V2 和 V3 同时出现,我们人为的规定先遍历数组小的节点也就是 V2。但是当我们使用栈的时候,如果让要保持先遍历数字小的节点,我们就必须要先让数字大的节点先入栈(因为栈的后进先出的原则)。

流程:

1)选取 V1,让 V1 进栈 。(此时栈内只有 V1 ,访问的节点依次为 V1)

2)弹出栈顶节点 V1 出栈,然后让它的邻接节点进栈;

3)V1 的邻接节点【V2 V3】, 我们让 V3 先进栈, V2 后压入 。(此时栈内从栈底到栈顶依次为 V3 V2 )

4)弹出栈顶的元素 V2 ,其邻接节点为【V1 V4 V5】,V1访问过了,让其为被访问的节点 V4, V5 进栈(此时栈内顶点依次为 V3 V5 V4;访问的节点依次为 V1--V2)

5)弹出栈顶的元素 V4 ,其邻接节点为【V2 V8】,V2访问过了,让其为被访问的节点 V8 进栈(此时栈内顶点依次为 V3 V5 V8;访问的节点依次为 V1-- V2 -- V4)

6)弹出栈顶的元素 V8 ,其邻接节点为【V4 V5】,V4,V5 都进过栈内,因此无节点入栈(此时栈内顶点依次为 V3 V5 V8;访问的节点依次为 V1-- V2 -- V4 -- V8)

7)弹出栈顶的元素 V5 ,其邻接节点为【V2 】,V2访问过了,因此无节点入栈(此时栈内顶点为 V3 ;访问的节点依次依次为 V1-- V2 -- V4 -- V8 -- V5)

8)弹出栈顶的元素 V3,其邻接节点为【V1 V6 V7】,V1访问过了,让其为被访问的节点 V6, V,7 进栈(此时栈内顶点依次为 V7 V6 ;访问的节点依次为 V1-- V2 -- V4 -- V8 -- V5 -- V3)

9)弹出栈顶的元素 V6,其邻接节点为【V3 V7】,V3,V7 都进过栈内,因此无节点入栈(此时栈内顶点依次为 V7  ;访问的节点依次为 V1-- V2 -- V4 -- V8 -- V5 -- V3 -- V6)

10)弹出栈顶的元素 V7,其邻接节点为【V3 V6】,V3,V6都进过栈内,因此无节点入栈(此时栈内顶点依次为无 ;访问的节点依次为 V1-- V2 -- V4 -- V8 -- V5 -- V3 -- V6 -- V7)

因此最终遍历结果为: V1-- V2 -- V4 -- V8 -- V5 -- V3 -- V6 -- V7

                               

                                    

---------------------------------------------------------------------------------------------------------------------------

参考:https://blog.csdn.net/wang725/article/details/82120428

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值