图的 DFS 与 BFS 复杂度分析

DFS的复杂度分析:

对于邻接表的存储方式:因为邻接表中每条链表上的从第2个结点到表尾结点一定是表头结点的邻接点,所以遍历表头结点的邻接的过程中只需要遍历这些顶点即可,无需遍历其他的顶点,所以遍历某个顶点的所有邻接点的复杂度为O(ei), ei为每个顶点的邻接点个数,也就是每条链表的边数。所以邻接表版的 dfs 遍历所有邻接点的时间复杂度为 O(e1 + e2 + e3 + .... + en) ,因为所有边数之和为 E , 所以时间复杂度为 O(E) , 又因为访问每个顶点都必须被访问一次, 比如设置vis[i] = true, 这个操作一共要执行 V  次,所以,设置所有顶点为已访问的时间复杂度为O(V), 所有总的时间为查找所有邻接点的时间加上设置每个顶点为已访问,总的时间为 O(E) + O(V) = O(E + V)。

邻接表的 dfs 递归的最大深度就是 链表条数。最大深度的情况发生在 遍历第一条链表的第一个邻接结点时,发现它还未被访问过,所以递归访问它,而递归访问它的第一个邻接点又发现它还没被访问过,又会去递归访问这个邻接点,这样,每次递归开始查找到的都是未访问过的结点,每次都会进行向下递归,每次递归层数都加一,这样递归层数就为链表条数。

void dfs(u){
    vis[u] = true;
    for(遍历顶点u的所有邻接点,即遍历u为表头的那条链表){        // 每轮时间复杂度为 O(ei)
         if(该邻接点未被访问){
            dfs(v)
         }
    }
}

邻接矩阵与邻接表遍历过程不同在于,对于邻接矩阵来说查找某个顶点的所有邻接点的过程必须遍历所有顶点,无论是否邻接都必须判断一次。所以查找每个顶点的邻接点的时间复杂度都为O(n), 共有 n 个顶点,这 n 个顶点就表现为递归深度最大为 n, 所以总的时间复杂度为 O(n + n + ... + n) = O(n ^2), 而设置每个顶点为已访问的时间复杂度为O(n), 所以总的时间复杂度为O((n^2 + n), 忽略小阶复杂度,保留大阶复杂度,所以我们通常说它的时间复杂度为 O(n^2)。

邻接矩阵版的 dfs 递归的最大深度是 顶点个数,情况发生的场景和邻接表最大深度的情况类似,每次递归查找到的第一个邻接点都是未访问过的,这样每次都会进行向下递归,每次递归层数都加一,最多递归 n 层。

void dfs(u){
    vis[u] = true;        // 设置 u 顶点为已访问
    for(int v = 0; v < n; v++){            // 每轮的时间复杂度为 O(n)
        if(vis[v] == false && G[u][v] != inf){
            dfs(v)
        }
    }
}

BFS的复杂度分析

有了上面 DFS 的复杂度分析,BFS 复杂度分析就很简单了,

BFS是一种借用队列来存储的过程,分层查找,优先考虑距离出发点近的点。无论是在邻接表还是邻接矩阵中存储,都需要借助一个辅助队列,v个顶点均需入队,最坏的情况下,空间复杂度为O(v)。

邻接表形式存储时,每个顶点均需搜索一次,每次结点被入队一次,出队一次,所以时间复杂度是 O(V),但是遍历某个顶点所有邻接点的复杂度是 O(ei), ei 为第 i 条链表的弧的条数,也就是邻接点个数,所以查找所有邻接点的复杂度为 O(e1 + e2 + ... + en) = O(E), 加上对每个结点进行入队出队的复杂度 O(V), 所以总的时间复杂度为O(E + V).

void bfs(int s){    // s 为选定的遍历图的起点
    Queue<int> queue;    // 定义一个队列
    queue.push(s);
    while(!queue.empty()){
        int top = queue.top();    // 出队队首元素
        queue.pop();
        for(访问 top 的所有邻接点){        // 每轮的时间复杂度为 O(ei)
            if(如果该邻接点未曾入队){
                将该结点入队;
                }
        }
    }
}

邻接矩阵存储方式时,查找每个顶点的邻接点所需时间为O(V),即该节点所在的该行的所有列。又因为有n个顶点,查找所有邻接点的时间复杂度为O(V^2), 加上对每个结点进行入队出队的复杂度 O(V), 所以总的时间复杂度为O(V^2 + V)。省略低阶复杂度,最终复杂度为 O(V^2).

void bfs(int s){    // s 为选定的遍历图的起点
    Queue<int> queue;    // 定义一个队列
    queue.push(s);
    while(!queue.empty()){
        int top = queue.top();    // 出队队首元素
        queue.pop();
        for(访问 top 的所有邻接点){        // 每轮的时间复杂度为 O(n)
            if(如果该邻接点未曾入队){
                将该结点入队;
                }
        }
    }
}

可以看到 DFS 和 BFS 的遍历方式,每个版本的时间复杂度是相同的。

这里再附上严蔚敏的教材上对 DFS 和 BFS 的复杂度分析:

DFS:

BFS:

文章参考:

数据结构:图之DFS与BFS的复杂度分析

图dfs和bfs时的时间复杂度

原文地址:https://www.cnblogs.com/hi3254014978/p/12627861.html

时间: 2024-08-27 00:27:04

图的 DFS 与 BFS 复杂度分析的相关文章

图的DFS与BFS遍历

一.图的基本概念 1.邻接点:对于无向图无v1 与v2之间有一条弧,则称v1与v2互为邻接点:对于有向图而言<v1,v2>代表有一条从v1到v2的弧,则称v2为v1的邻接点. 2.度:就是与该顶点相互关联的弧的个数. 3.连通图:无向图的每个顶点之间都有可达路径,则称该无向图为连通图.有向图每个顶点之间都有<v1,v2>和<v2,v1>,则称此有向图为强连通图. 二.存储结构 1.邻接矩阵存储(Adjacency Matrix) 对无权图,顶点之间有弧标1,无弧标0:

学习笔记:图的DFS和BFS的两种搜索办法

  在学习图结构的过程中,DFS和BFS是两种不同的遍历方式,其寻找元素具有不同的优点和缺陷. BFS被称作广度优先算法, 在遍历整个图的过程中,BFS将采用入队的方式进行,值得一提的是,这和树结构中的层序遍历有很大的相似之处. 在层序遍历中,将父亲节点入队后,在父亲节点出队后,将其儿子节点入队. 同理在图的BFS遍历中,先让BFS的首元素入队,在收元素入队后将他的儿子节点入队,放能够实现BFS搜索,他们的整体思想是一样的. 1 void TraversalGraph_BFS(LGraph Gr

数据结构(11) -- 邻接表存储图的DFS和BFS

/////////////////////////////////////////////////////////////// //图的邻接表表示法以及DFS和BFS /////////////////////////////////////////////////////////////// #include <iostream> #include <stdlib.h> #include <queue> using namespace std; //图的邻接表表示法

树的常见算法&amp;图的DFS和BFS

树及二叉树: 树:(数据结构中常见的树) 树的定义 树的存储:下面介绍三种不同的树的表示法:双亲表示法,.孩子表示法,.孩子兄弟表示法. 双亲表示法 我们假设以一组连续空间存储树的结点,同时在每个结点中,附设一个指示器指向其双亲结点到链表中的位置.也就是说每个结点除了知道自己之外还需要知道它的双亲在哪里. 它的结构特点是如图所示: 以下是我们的双亲表示法的结构定义代码: /*树的双亲表示法结点结构定义 */ #define MAXSIZE 100 typedef int ElemType; //

图、dfs、bfs

graphdfsbfs 1.clone graph2.copy list with random pointer3.topological sorting4.permutations5.subsets6.n queens7.subsetsII 8.palindrome partitioning9.combination sum10.combination sumII11.word ladder 12.word ladderII 克隆图:先克隆点,再克隆边. 宽度优先搜索有模板,以后告诉了图中的一

图的DFS与BFS

1.DFS 深度优先搜索在搜索过程中访问某个顶点后,需要递归地访问此顶点的所有未访问过的相邻顶点. (1)递归实现 #include <iostream> #define N 5 using namespace std; int maze[N][N] = { { 0, 1, 1, 0, 0 }, { 0, 0, 1, 0, 1 }, { 0, 0, 1, 0, 0 }, { 1, 1, 0, 0, 1 }, { 0, 0, 1, 0, 0 } }; int visited[N + 1] = {

搜索分析(DFS、BFS、递归、记忆化搜索)

搜索分析(DFS.BFS.递归.记忆化搜索) 1.线性查找 在数组a[]={0,1,2,3,4,5,6,7,8,9,10}中查找1这个元素. (1)普通搜索方法,一个循环从0到10搜索,这里略. (2)递归(从中间向两边) 1 //递归一定要写成记忆化递归 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool vis[11]; 5 int count1=0; 6 7 void search(int n){ 8 count1++; 9

数据结构(12) -- 图的邻接矩阵的DFS和BFS

//////////////////////////////////////////////////////// //图的邻接矩阵的DFS和BFS //////////////////////////////////////////////////////// #include <iostream> #include <stdlib.h> #include <queue> #define MaxVertexNum 100 //最大顶点数 //#define INFINI

图的遍历(bfs 和dfs)

BFS的思想: 从一个图的某一个顶点V0出发,首先访问和V0相邻的且未被访问过的顶点V1.V2.……Vn,然后依次访问与V1.V2……Vn相邻且未被访问的顶点.如此继续,找到所要找的顶点或者遍历完整个图. 由此可以看出,用BFS进行搜索所搜索的顶点都是按深度进行扩展的,先找到到V0距离为1的所有顶点,然后找到距离V0为2的顶点……所以BFS所搜索到的都是最短的路径. 由于要将距离V0为d(d>0)的且未被方位的点都记录起来,我们采用队列这种数据结构.队列的特点是先进先出(FIFO),从某个顶点出