什么时候需要用到BFS呢?通常图的遍历(Traversal in Graph)
主要有以下四种情况:
另外,BFS还有双向BFS, DFS却没有双向DFS。双向BFS适用于如下场景:
什么是用到DFS呢?
注意:
我们以subsets举例 (引用自九章):
搜索时间复杂度公式:O(解的个数 * 产生解的复杂度), 因为解的平均长度是n/2,所以扩2倍也没有关系,可以认为是n的。 实际复杂度是O(2^n * n / 2) = O(2^(n-1) * n), 这是根据公式估算的。具体来看这个复杂度用数学推到如何算出来;
严格意义上来讲是复杂度是这样的:
**1 * 长度为1的解的个数 + 2 * 长度为2的解的个数 + 3 * 长度为3的解的个数 + n * 长度为n的解的个数 = **
1 * c(n, 1) + 2 * c(n, 2) + … + n * c(n,n)
// c(m,n) 是排列组合中的组合
又因为
i * C(n, i) = i * n! / [i! (n-i)!] = n! / [(i-1)! (n-i)!] = n * (n-1)! / [(i-1)! (n-i)!] = n * C(n-1,i-1)
所以
**1 * c(n, 1) + 2 * c(n, 2) + … + n * c(n,n) = sum(i * c(n, i)) = sum(n * c(n - 1, i - 1)) = n * sum(c(n-1, i-1)) **
其中 1 <= i <=n
sum(c(n - 1, i - 1)) 就是杨辉三角的某一层的求和 ,为2^(n - 1)
最后得出:1 * c(n, 1) + 2 * c(n, 2) + … + n * c(n,n) = 2^(n - 1) * n
跟我们的估算完全一致。有时候不需要数学推导,因为是大O,又是搜索算法,故用我们的公式估计一个大概就可以了。
permutations 可以用类似的方式推导。
注意这里是搜索的时间复杂度估计的公式,并不是特别指DFS的,BFS也适合。
对于BFS,因为每个点进入queue一次,加上每个点出来时候处理的复杂度,可以认为是O(点的个数 * 每个点处理的平均复杂度)
再对比一下DP。DP的时间复杂度是O(状态总数×计算每个状态时间复杂度)。
5) 关于BFS和DFS的空间复杂度
BFS的空间复杂度是同一层的最大宽度(最坏情况所有点都在一层,所以是O(n))。
DFS的空间复杂度取决于深度,一般堆和栈都要计算在内。但有时人们认为系统栈很小,所以只算堆空间。
对于排列组合问题的空间复杂度来说,一般DFS优于BFS。因为排列的所有解个数是O(n!),组合的所有解个数是O(2^n) 。
而DFS递归的深度是O(n),即DFS的空间复杂度是O(n)。而BFS对排列最坏空间复杂度是排列O(n!),对组合的最坏空间复杂度是O(2^n)。
下面内容引用自
https://blog.csdn.net/Charles_ke/article/details/82497543
v为图的顶点数,E为边数。
BFS的复杂度分析:
BFS是一种借用队列来存储的过程,分层查找,优先考虑距离出发点近的点。无论是在邻接表还是邻接矩阵中存储,都需要借助一个辅助队列,v个顶点均需入队,最坏的情况下,空间复杂度为O(v)。
邻接表形式存储时,每个顶点均需搜索一次,时间复杂度T1=O(v),从一个顶点开始搜索时,开始搜索,访问未被访问过的节点。最坏的情况下,每个顶点至少访问一次,每条边至少访问1次,这是因为在搜索的过程中,若某结点向下搜索时,其子结点都访问过了,这时候就会回退,故时间复 杂度为O(E),算法总的时间复 度为O(|V|+|E|)。
邻接矩阵存储方式时,查找每个顶点的邻接点所需时间为O(V),即该节点所在的该行该列。又有n个顶点,故算总的时间复杂度为O(|V|^2)。
DFS复杂度分析
DFS算法是一一个递归算法,需要借助一个递归工作栈,故它的空问复杂度为O(V)。
遍历图的过程实质上是对每个顶点查找其邻接点的过程,其耗费的时间取决于所采用结构。
邻接表表示时,查找所有顶点的邻接点所需时间为O(E),访问顶点的邻接点所花时间为O(V),此时,总的时间复杂度为O(V+E)。
邻接矩阵表示时,查找每个顶点的邻接点所需时间为O(V),要查找整个矩阵,故总的时间度为O(V^2)。