数据结构6——DFS

一、相关定义

深度优先遍历,也有称为深度优先搜索,简称DFS。其实,就像是一棵树的前序遍历。

初始条件:图G所有顶点均未被访问过,任选一点v。

遍历过程:它从图中某个结点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中的所有顶点都被访问到为止。

DFS适合此类题目

  • 给定初始状态跟目标状态,要求判断从初始状态到目标状态是否有解。

二、算法过程

以如下图的无向图G4为例,进行图的深度优先搜索:

假设从顶点v1出发进行搜索,在访问了顶点v1之后,选择邻接点v2。因为v2未曾访问,则从v2出发进行搜索。依次类推,接着从v、v8 、v5出发进行搜索。在访问了v5之后,由于v5的邻接点都已被访问,则搜索回到v8。由于同样的理由,搜索继续回到v4,v2直至v1,此时由于v1的另一个邻接点未被访问,则搜索又从v1到v3,再继续进行下去由此,得到的顶点访问序列为:

显然,这是一个递归的过程。为了在遍历过程中便于区分顶点是否已被访问,需附设访问标志数组vis[0…n-1], ,其初值为false,一旦某个顶点被访问,则其相应的分量置为true。

三、代码实现

我们用邻接矩阵的方式,则代码如下所示。

/*    图的DFS遍历    */
//邻接矩阵形式实现
//顶点从1开始
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 105;        //最大顶点数
typedef int VertexType;     //顶点类型
bool vis[maxn];  

struct Graph{               //邻接矩阵表示的图结构
    VertexType vex[maxn];   //存储顶点
    int arc[maxn][maxn];    //邻接矩阵
    int vexnum,arcnum;      //图的当前顶点数和弧数
};

void createGraph(Graph &g)  //构建无向图
{
    cout<<"请输入顶点数和边数:";
    cin>>g.vexnum>>g.arcnum;

    //构造顶点向量
    cout<<"请依次输入各顶点:\n";
    for(int i=1;i<=g.vexnum;i++){
        scanf("%d",&g.vex[i]);
    }

    //初始化邻接矩阵
    for(int i=1;i<=g.vexnum;i++){
        for(int j=1;j<=g.vexnum;j++){
            g.arc[i][j] = 0;
        }
    }

    //构造邻接矩阵
    VertexType u,v;     //分别是一条弧的弧尾(起点)和弧头(终点)
    printf("每一行输入一条弧依附的顶点(空格分开):\n");
    for(int i=1;i<=g.arcnum;i++){
        cin>>u>>v;
        g.arc[u][v] = g.arc[v][u] = 1;
    }
}

//邻接矩阵的深度优先递归算法
void DFS(Graph g,int i)
{
    vis[i] = true;
    printf("%d\t",g.vex[i]);                //打印顶点
    for(int j=1;j<=g.vexnum;j++){            //遍历每个顶点
        if(g.arc[i][j]==1 && !vis[j]){        //如果顶点j是顶点i的未访问的邻接点
            DFS(g,j);                        //深度优先搜索顶点j
        }
    }
}

//邻接矩阵的深度遍历操作
void DFSTraverse(Graph g)
{
    for(int i=1;i<=g.vexnum;i++){
        vis[i] = false;                        //初始化所有顶点状态都是未访问过状态
    }
    for(int i=1;i<=g.vexnum;i++){
        if(!vis[i]){
            DFS(g,i);                        //对未访问的顶点调用DFS,若是连通图,只会执行一次
        }
    }
}

int main()
{
    Graph g;
    createGraph(g);
    DFSTraverse(g);
    return 0;
}

如果使用的是邻接表存储结构,其DFSTraverse函数的代码几乎是相同的,只是在递归函数中因为将数组换成了链表而有不同,代码如下。

//邻接表的深度递归算法
void DFS(GraphList g, int i)
{
    EdgeNode *p;
    vis[i] = true;
    printf("%d ", g->adjList[i].data);   //打印顶点,也可以其他操作
    p = g->adjList[i].firstedge;
    while(p)
    {
        if(!vis[p->adjvex])
        {
            DFS(g, p->adjvex);           //对访问的邻接顶点递归调用
        }
        p = p->next;
    }
}

//邻接表的深度遍历操作
void DFSTraverse(GraphList g)
{
    int i;
    for(i = 0; i < g.numVertexes; i++)
    {
        vis[i] = false;
    }
    for(i = 0; i < g.numVertexes; i++)
    {
        if(!vis[i])
        {
            DFS(g, i);
        }
    }
}

分析上述算法,在遍历时,对图中每个顶点至多调用一次DFS 函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。因此,遍历图的过程实质上是对每个顶点查找其邻接点的过程。其耗费的时间则取决于所采用的存储结构。

当用二维数组表示邻接矩阵图的存储结构时,查找每个顶点的邻接点所需时间为O(n2) ,其中n为图中顶点数。

而当以邻接表作图的存储结构时,找邻接点所需时间为O(e),其中e 为无向图中边的数或有向图中弧的数。由此,当以邻接表作存储结构时,深度优先搜索遍历图的时间复杂度为O(n+e) 。  

对比两个不同的存储结构的深度优先遍历算法,显然对于点多边少的稀疏图来说,邻接表结构使得算法在时间效率上大大提高。

四、沙场练兵

题目一、滑雪

题目二、棋盘问题

五、知识扩展

不知道你注意到没,在深度/广度搜索的过程中,其实相邻节点的加入如果是有一定策略的话,对算法的效率是有很大影响的,你可以做一下简单马周游跟马周游这两个题,你就有所体会,你会发现你在搜索的过程中,用一定策略去访问相邻节点会提升很大的效率。 这些运用到的贪心的思想,你可以再看看启发式搜索的算法,例如A*算法等。

时间: 2024-10-07 12:50:29

数据结构6——DFS的相关文章

【数据结构】DFS求有向图的强连通分量

用十字链表结构写的,根据数据结构书上的描述和自己的理解实现.但理解的不透彻,所以不知道有没有错误.但实验了几个都ok. #include <iostream> #include <vector> using namespace std; //有向图十字链表表示 #define MAX_VERTEX_NUM 20 typedef struct ArcBox{ int tailvex, headvex; //该弧尾和头顶点的位置 struct ArcBox *hlink, *tlink

数据结构之dfs

dfs: 注意的小问题,如果是多路径的话,一次memset(vis,o,sizeof(vis))如果跟步骤有关要申请一个全局变量 理解栈与递归 hdu  1518数据拼正方形:思路:就是排序,首先可以剪枝,就是sum%4!=0的,有个小的处理就是先求出sum/4:#include <iostream>#include <algorithm>using namespace std;bool cmp(int a,int b){    return a>b?true:false;}

数据结构之DFS与BFS

深度搜索(DFS) and  广度搜索(BFS) 代码如下: 1 #include "stdafx.h" 2 #include<iostream> 3 #include<string> 4 using namespace std; 5 #define MAX 30 6 #define MVNum 100 7 #define ERROR 1 8 typedef char VerTexType; 9 typedef int Status; 10 typedef in

【目录】数据结构

数据结构 [数据结构]DFS求有向图的强连通分量 [数据结构]二叉堆 [数据结构]离散事件模拟 [数据结构]book3_3 表达式求值 3.19 [数据结构]红黑树 C语言代码

数据结构学习笔记05图 (邻接矩阵 邻接表--&gt;BFS DFS)

数据结构之图 图(Graph) 包含 一组顶点:通常用V (Vertex) 表示顶点集合 一组边:通常用E (Edge) 表示边的集合 边是顶点对:(v, w) ∈E ,其中v, w ∈ V 有向边<v, w> 表示从v指向w的边(单行线) 不考虑重边和自回路 无向图:边是无向边(v, w) 有向图:边是有向边<v, w> 连通:如果从V到W存在一条(无向)路径,则称V和W是连通的 连通图(Connected Graph):如果对于图的任一两个顶点v.w∈V,v和w都是连通的,则称

数据结构之 图论---连通分量的个数(dfs搜索)

数据结构实验:连通分量个数 Time Limit: 1000MS Memory limit: 65536K 题目描述 在无向图中,如果从顶点vi到顶点vj有路径,则称vi和vj连通.如果图中任意两个顶点之间都连通,则称该图为连通图, 否则,称该图为非连通图,则其中的极大连通子图称为连通分量,这里所谓的极大是指子图中包含的顶点个数极大. 例如:一个无向图有5个顶点,1-3-5是连通的,2是连通的,4是连通的,则这个无向图有3个连通分量. 输入 第一行是一个整数T,表示有T组测试样例(0 < T <

数据结构(15):图 深度优先遍历(DFS)

/*-----------------------------------------------*/ /* 邻接矩阵的DFS */ // 基于 数据结构(14) 中的邻接矩阵的结构 #include <iostream> using namespace std; typedef char VertexType; typedef int EdgeType; const int MAXVEX = 100; const int INFINITY = 65535; typedef struct {

rwkj 1501 数据结构:图的DFS遍历

数据结构:图的DFS遍历 时间限制(普通/Java):1000MS/3000MS            运行内存限制:65536KByte 总提交:259            测试通过:183 描述 从已给的连通图中某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历.图的遍历的遍历有DFS和BFS两种. 上面的图,从顶点0出发,按照顶点序号从小到大的顺序DFS,得到遍历顺序为0 1 2 3  4 5 6 7 8. 输入 输入图的顶点个数(<20)与边数,以及每

图的BFS和DFS在数据结构为邻接矩阵时的实现

#include <iostream> #include <queue> using namespace std; //因为只是为了练习DFS和BFS 数据结构定义的很随意 没有优化类啦 能用就行 class Graph { private: static const int MAX=100; int weight[MAX][MAX];//任意一个图最大节点数为MA int nodes; int vertexs; bool *isVisited; public: void init