迭代深化搜索(IDS)或迭代深化深度优先搜索(IDDFS)
遍历图有两种常见的方式,BFS 和 DFS。考虑到高度和宽度巨大的树(或图),由于以下原因,BFS 和 DFS 都不是很有效。
- DFS首先遍历经过根的一个相邻节点,然后是下一个相邻节点。这种方法的问题是,如果有一个靠近根的节点,但不在 DFS 探索的前几个子树中,那么 DFS 很晚才到达该节点。此外,DFS 可能找不到到节点的最短路径(就边数而言)。
- BFS逐级进行,但需要更多空间。 DFS 所需的空间是 O(d),其中 d 是树的深度,但 BFS 所需的空间是 O(n),其中 n 是树中的节点数(为什么?请注意,树的最后一层可以有大约 n/ 2 个节点和倒数第二级 n/4 个节点,在 BFS 中,我们需要将每个级别一个接一个地放入队列中)。
IDDFS结合了深度优先搜索的空间效率和广度优先搜索的快速搜索(对于更接近根的节点)。
IDDFS 是如何工作的?
IDDFS 从初始值开始调用不同深度的 DFS。在每次调用中,DFS 都被限制超出给定的深度。所以基本上我们以 BFS 方式进行 DFS。
算法:
// Returns true if target is reachable from
// src within max_depth
bool IDDFS(src, target, max_depth)
for limit from 0 to max_depth
if DLS(src, target, limit) == true
return true
return false
bool DLS(src, target, limit)
if (src == target)
return true;
// If reached the maximum depth,
// stop recursing.
if (limit <= 0)
return false;
foreach adjacent i of src
if DLS(i, target, limit?1)
return true
return false
需要注意的重要一点是,我们多次访问顶级节点。最后一个(或最大深度)级别被访问一次,倒数第二个级别被访问两次,依此类推。它可能看起来很昂贵,但事实证明它并没有那么昂贵,因为在树中,大多数节点都位于底层。因此,是否多次访问上层并不重要。
下面是上述算法的实现
C/C++
// C++ program to search if a target node is reachable from
// a source with given max depth.
#include
using namespace std;
// Graph class represents a directed graph using adjacency
// list representation.
class Graph
{
int V; // No. of vertices
// Pointer to an array containing
// adjacency lists
list *adj;
// A function used by IDDFS
bool DLS(int v, int target, int limit);
public:
Graph(int V); // Constructor
void addEdge(int v, int w);
// IDDFS traversal of the vertices reachable from v
bool IDDFS(int v, int target, int max_depth);
};
Graph::Graph(int V)
{
this->V = V;
adj = new list[V];
}
void Graph::addEdge(int v, int w)
{
adj[v].push_back(w); // Add w to v’s list.
}
// A function to perform a Depth-Limited search
// from given source 'src'
bool Graph::DLS(int src, int target, int limit)
{
if (src == target)
return true;
// If reached the maximum depth, stop recursing.
if (limit <= 0)
return false;
// Recur for all the vertices adjacent to source vertex
for (auto i = adj[src].begin(); i != adj[src].end(); ++i)
if (DLS(*i, target, limit-1) == true)
return true;
return false;
}
// IDDFS to search if target is reachable from v.
// It uses recursive DFSUtil().
bool Graph::IDDFS(int src, int target, int max_depth)
{
// Repeatedly depth-limit search till the
// maximum depth.
for (int i = 0; i <= max_depth; i++)
if (DLS(src, target, i) == true)
return true;
return false;
}
// Driver code
int main()
{
// Let us create a Directed graph with 7 nodes
Graph g(7);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(1, 4);
g.addEdge(2, 5);
g.addEdge(2, 6);
int target = 6, maxDepth = 3, src = 0;
if (g.IDDFS(src, target, maxDepth) == true)
cout << "Target is reachable from source "
"within max depth";
else
cout << "Target is NOT reachable from source "
"within max depth";
return 0;
}
Python
# Python program to print DFS traversal from a given
# given graph
from collections import defaultdict
# This class represents a directed graph using adjacency
# list representation
class Graph:
def __init__(self,vertices):
# No. of vertices
self.V = vertices
# default dictionary to store graph
self.graph = defaultdict(list)
# function to add an edge to graph
def addEdge(self,u,v):
self.graph[u].append(v)
# A function to perform a Depth-Limited search
# from given source 'src'
def DLS(self,src,target,maxDepth):
if src == target : return True
# If reached the maximum depth, stop recursing.
if maxDepth <= 0 : return False
# Recur for all the vertices adjacent to this vertex
for i in self.graph[src]:
if(self.DLS(i,target,maxDepth-1)):
return True
return False
# IDDFS to search if target is reachable from v.
# It uses recursive DLS()
def IDDFS(self,src, target, maxDepth):
# Repeatedly depth-limit search till the
# maximum depth
for i in range(maxDepth):
if (self.DLS(src, target, i)):
return True
return False
# Create a graph given in the above diagram
g = Graph (7);
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 3)
g.addEdge(1, 4)
g.addEdge(2, 5)
g.addEdge(2, 6)
target = 6; maxDepth = 3; src = 0
if g.IDDFS(src, target, maxDepth) == True:
print ("Target is reachable from source " +
"within max depth")
else :
print ("Target is NOT reachable from source " +
"within max depth")
# This code is contributed by Neelam Pandey
输出 :
Target is reachable from source within max depth
插图:
可能有两种情况——
a) 当图没有环时:这种情况很简单。我们可以使用不同的高度限制多次 DFS。
b) 当图有循环时。这很有趣,因为 IDDFS 中没有访问标志。
时间复杂度:假设我们有一棵树,其分支因子为“b”(每个节点的子节点数),其深度为“d”,即有b d个节点。
在迭代加深搜索中,最底层的节点被展开一次,下一层的节点被展开两次,以此类推,直到搜索树的根,展开d+1次。因此,迭代深化搜索中的扩展总数是-
(d)b + (d-1)b2 + .... + 3bd-2 + 2bd-1 + bd
That is,
Summation[(d + 1 - i) bi], from i = 0 to i = d
Which is same as O(bd)
在评估上述表达式后,我们发现渐近 IDDFS 与 DFS 和 BFS 所用的时间相同,但它确实比它们都慢,因为它在时间复杂度表达式中具有更高的常数因子。
IDDFS 最适合完整的无限树
参考:
https://en.wikipedia.org/wiki/Iterative_deepening_depth-first_search