Depth-first search: Difference between revisions
68.145.121.136 (talk) ←Blanked the page |
m Reverted edits by 68.145.121.136 to last version by Rror (using Huggle) |
||
Line 1: | Line 1: | ||
{{Infobox Algorithm |
|||
|class=[[Search algorithm]] |
|||
|image=[[Image:Depth-first-tree.svg|none|300px|Order in which the nodes get expanded]]<small>Order in which the nodes are expanded</small> |
|||
|data=[[Graph (data structure)|Graph]] |
|||
|time=<math>O (|V| + |E|) = O (b^d)</math> |
|||
|space=<math>O (h)</math> where <math>h</math> is the length of the longest [[Path (graph theory)|simple path]] in the graph. |
|||
|optimal=no |
|||
|complete=yes (unless infinite paths are possible) |
|||
}}{{graph search algorithm}} |
|||
'''Depth-first search''' ('''DFS''') is an [[algorithm]] for traversing or searching a [[tree data structure|tree]], [[tree structure]], or [[graph (data structure)|graph]]. One starts at the root (selecting some node as the root in the graph case) and explores as far as possible along each branch before [[backtracking]]. |
|||
Formally, DFS is an [[uninformed search]] that progresses by expanding the first child node of the search [[tree data structure|tree]] that appears and thus going deeper and deeper until a goal node is found, or until it hits a node that has no children. Then the search [[backtracking|backtracks]], returning to the most recent node it hadn't finished exploring. In a non-recursive implementation, all freshly expanded nodes are added to a [[LIFO]] [[stack (data structure)|stack]] for exploration. |
|||
Space [[complexity]] of DFS is much lower than BFS ([[breadth-first search]]). It also lends itself much better to [[heuristics|heuristic]] methods of choosing a likely-looking branch. Time [[complexity]] of both algorithms are proportional to the number of vertices plus the number of edges in the graphs they traverse (O(|V| + |E|)). |
|||
When searching large graphs that cannot be fully contained in memory, DFS suffers from non-termination when the length of a path in the search tree is infinite. The simple solution of "remember which nodes I have already seen" doesn't always work because there can be insufficient memory. This can be solved by maintaining an increasing limit on the depth of the tree, which is called [[iterative deepening depth-first search]]. |
|||
For the following graph: |
|||
[[Image:graph.traversal.example.svg|200px]] |
|||
a depth-first search starting at A, assuming that the left edges in the shown graph are chosen before right edges, and assuming the search remembers previously-visited nodes and will not repeat them (since this is a small graph), will visit the nodes in the following order: A, B, D, F, E, C, G. |
|||
Performing the same search without remembering previously visited nodes results in visiting nodes in the order A, B, D, F, E, A, B, D, F, E, etc. forever, caught in the A, B, D, F, E cycle and never reaching C or G. |
|||
Iterative deepening prevents this loop and will reach the following nodes on the following depths, assuming it proceeds left-to-right as above: |
|||
*0: A |
|||
*1: A (repeated), B, C, E |
|||
(Note that iterative deepening has now seen C, when a conventional depth-first search did not.) |
|||
*2: A, B, D, F, C, G, E, F |
|||
(Note that it still sees C, but that it came later. Also note that it sees E via a different path, and loops back to F twice.) |
|||
*3: A, B, D, F, E, C, G, E, F, B |
|||
For this graph, as more depth is added, the two cycles "ABFE" and "AEFB" will simply get longer before the algorithm gives up and tries another branch. |
|||
==Output of a depth-first search== |
|||
[[Image:Tree_edges.svg|thumb|270px|The four types of tree edges]] |
|||
The most natural result of a depth first search of a graph (if it is considered as a [[function (mathematics)|function]] rather than a [[subroutine|procedure]]) is a [[spanning tree (mathematics)|spanning tree]] of the vertices reached during the search. Based on this spanning tree, the edges of the original graph can be divided into three classes: '''forward edges''', which point from a node of the tree to one of its descendants, '''back edges''', which point from a node to one of its ancestors, and '''cross edges''', which do neither. Sometimes '''tree edges''', edges which belong to the spanning tree itself, are classified separately from forward edges. It can be shown that if the graph is undirected then all of its edges are tree edges or back edges. |
|||
===Vertex orderings=== |
|||
It is also possible to use the depth-first search to linearly order the vertices (or nodes) of the original graph (or tree). There are three common ways of doing this: |
|||
* A '''preordering''' is a list of the vertices in the order that they were first visited by the depth-first search algorithm. This is a compact and natural way of describing the progress of the search, as was done earlier in this article. A preordering of an expression tree is the expression in [[Polish notation]]. |
|||
* A '''postordering''' is a list of the vertices in the order that they were ''last'' visited by the algorithm. A postordering of an [[parse tree|expression tree]] is the expression in [[reverse Polish notation]]. |
|||
* A '''reverse postordering''' is the reverse of a postordering, i.e. a list of the vertices in the opposite order of their last visit. When searching a tree, reverse postordering is the same as preordering, but in general they are different when searching a graph. For example, when searching the graph |
|||
: [[Image:If-then-else-control-flow-graph.svg]] |
|||
: beginning at node A, the possible preorderings are A B D C and A C D B (depending upon whether the algorithm chooses to visit B or C first), while the possible reverse postorderings are A B C D and A C B D. Reverse postordering produces a [[topological sorting]] of any [[directed acyclic graph]]. This ordering is also useful in [[control flow graph|control flow analysis]] as it often represents a natural linearization of the control flow. The graph above might represent the flow of control in a code fragment like |
|||
if ('''A''') then { |
|||
'''B''' |
|||
} else { |
|||
'''C''' |
|||
} |
|||
'''D''' |
|||
: and it is natural to consider this code in the order A B C D or A C B D, but not natural to use the order A B D C or A C D B. |
|||
==Pseudocode== |
|||
A recursive version of the algorithm: |
|||
def dfs('''v'''): |
|||
mark '''v''' as visited |
|||
preorder-process('''v''') |
|||
for all vertices '''i''' adjacent to '''v''' such that '''i''' not visited |
|||
dfs('''i''') |
|||
postorder-process('''v''') |
|||
Another version, without the [[recursion]]: |
|||
dfs('''graph''' G) |
|||
{ |
|||
'''list''' L = '''empty''' |
|||
'''tree''' T = '''empty''' |
|||
''choose a starting vertex x'' |
|||
search(x) |
|||
'''while'''(L '''is not empty''') |
|||
{ |
|||
''remove edge (v, w) from beginning of L'' |
|||
'''if''' ''w not yet visited'' |
|||
{ |
|||
''add (v, w) to T'' |
|||
search(w) |
|||
} |
|||
} |
|||
} |
|||
search('''vertex''' v) |
|||
{ |
|||
''visit v'' |
|||
''for each edge (v, w)'' |
|||
''add edge (v, w) to the beginning of L'' |
|||
} |
|||
==Applications== |
|||
Here are some algorithms where DFS is used: |
|||
* Finding [[Connected component (graph theory)|connected components]]. |
|||
* [[Topological sorting]]. |
|||
* Finding 2-(edge or vertex)-connected components. |
|||
* Finding [[strongly connected components]]. |
|||
* Solving puzzles with only one solution, such as [[Maze|mazes]]. |
|||
== External links == |
|||
* [http://www.cse.ohio-state.edu/~gurari/course/cis680/cis680Ch14.html Depth-First Explanation and Example] |
|||
* [http://www.boost.org/libs/graph/doc/depth_first_search.html C++ Boost Graph Library: Depth-First Search] |
|||
* [http://www.cs.duke.edu/csed/jawaa/DFSanim.html Depth-First Search Animation (for a directed graph)] |
|||
* [http://www.kirupa.com/developer/actionscript/depth_breadth_search.htm Depth First and Breadth First Search: Explanation and Code] |
|||
* [http://www.cs.usask.ca/resources/tutorials/csconcepts/1999_8/tutorial/beginner/trees/breadth.html dfs applet] |
|||
== References == |
|||
<div class="references-small"> |
|||
* [[Thomas H. Cormen]], [[Charles E. Leiserson]], [[Ronald L. Rivest]], and [[Clifford Stein]]. ''[[Introduction to Algorithms]]'', Second Edition. MIT Press and McGraw-Hill, 2001. ISBN 0-262-03293-7. Section 22.3: Depth-first search, pp.540–549. |
|||
</div> |
|||
* {{ Citation |
|||
| last=[[Knuth]] |
|||
| first=Donald E. |
|||
| title=The Art Of Computer Programming Vol 1. 3rd ed. |
|||
| publisher=Addison-Wesley |
|||
| place=Boston |
|||
| year=1997 |
|||
| ISBN=0-201-89683-4 |
|||
| url=http://www-cs-faculty.stanford.edu/~knuth/taocp.html |
|||
}} |
|||
[[Category:Graph algorithms]] |
|||
[[Category:Search algorithms]] |
|||
[[Category:Articles with example pseudocode]] |
|||
[[ca:Cerca en profunditat]] |
|||
[[de:Tiefensuche]] |
|||
[[es:Búsqueda en profundidad]] |
|||
[[fr:Algorithme de parcours en profondeur]] |
|||
[[he:אלגוריתם חיפוש לעומק]] |
|||
[[lt:Paieška į gylį]] |
|||
[[nl:Diepte-eerst zoeken]] |
|||
[[ja:深さ優先探索]] |
|||
[[pl:Przeszukiwanie w głąb]] |
|||
[[pt:Busca em profundidade]] |
|||
[[ru:Поиск в глубину]] |
|||
[[fi:Syvyyssuuntainen läpikäynti]] |
|||
[[vi:Tìm kiếm theo chiều sâu]] |
|||
[[uk:Пошук в глибину]] |
|||
[[zh:深度优先搜索]] |
Revision as of 20:07, 15 June 2008
![]() | |
Class | Search algorithm |
---|---|
Data structure | Graph |
Worst-case performance | |
Worst-case space complexity | where is the length of the longest simple path in the graph. |
Optimal | no |
Template:Graph search algorithm
Depth-first search (DFS) is an algorithm for traversing or searching a tree, tree structure, or graph. One starts at the root (selecting some node as the root in the graph case) and explores as far as possible along each branch before backtracking.
Formally, DFS is an uninformed search that progresses by expanding the first child node of the search tree that appears and thus going deeper and deeper until a goal node is found, or until it hits a node that has no children. Then the search backtracks, returning to the most recent node it hadn't finished exploring. In a non-recursive implementation, all freshly expanded nodes are added to a LIFO stack for exploration.
Space complexity of DFS is much lower than BFS (breadth-first search). It also lends itself much better to heuristic methods of choosing a likely-looking branch. Time complexity of both algorithms are proportional to the number of vertices plus the number of edges in the graphs they traverse (O(|V| + |E|)).
When searching large graphs that cannot be fully contained in memory, DFS suffers from non-termination when the length of a path in the search tree is infinite. The simple solution of "remember which nodes I have already seen" doesn't always work because there can be insufficient memory. This can be solved by maintaining an increasing limit on the depth of the tree, which is called iterative deepening depth-first search.
For the following graph:
a depth-first search starting at A, assuming that the left edges in the shown graph are chosen before right edges, and assuming the search remembers previously-visited nodes and will not repeat them (since this is a small graph), will visit the nodes in the following order: A, B, D, F, E, C, G.
Performing the same search without remembering previously visited nodes results in visiting nodes in the order A, B, D, F, E, A, B, D, F, E, etc. forever, caught in the A, B, D, F, E cycle and never reaching C or G.
Iterative deepening prevents this loop and will reach the following nodes on the following depths, assuming it proceeds left-to-right as above:
- 0: A
- 1: A (repeated), B, C, E
(Note that iterative deepening has now seen C, when a conventional depth-first search did not.)
- 2: A, B, D, F, C, G, E, F
(Note that it still sees C, but that it came later. Also note that it sees E via a different path, and loops back to F twice.)
- 3: A, B, D, F, E, C, G, E, F, B
For this graph, as more depth is added, the two cycles "ABFE" and "AEFB" will simply get longer before the algorithm gives up and tries another branch.
Output of a depth-first search

The most natural result of a depth first search of a graph (if it is considered as a function rather than a procedure) is a spanning tree of the vertices reached during the search. Based on this spanning tree, the edges of the original graph can be divided into three classes: forward edges, which point from a node of the tree to one of its descendants, back edges, which point from a node to one of its ancestors, and cross edges, which do neither. Sometimes tree edges, edges which belong to the spanning tree itself, are classified separately from forward edges. It can be shown that if the graph is undirected then all of its edges are tree edges or back edges.
Vertex orderings
It is also possible to use the depth-first search to linearly order the vertices (or nodes) of the original graph (or tree). There are three common ways of doing this:
- A preordering is a list of the vertices in the order that they were first visited by the depth-first search algorithm. This is a compact and natural way of describing the progress of the search, as was done earlier in this article. A preordering of an expression tree is the expression in Polish notation.
- A postordering is a list of the vertices in the order that they were last visited by the algorithm. A postordering of an expression tree is the expression in reverse Polish notation.
- A reverse postordering is the reverse of a postordering, i.e. a list of the vertices in the opposite order of their last visit. When searching a tree, reverse postordering is the same as preordering, but in general they are different when searching a graph. For example, when searching the graph
- beginning at node A, the possible preorderings are A B D C and A C D B (depending upon whether the algorithm chooses to visit B or C first), while the possible reverse postorderings are A B C D and A C B D. Reverse postordering produces a topological sorting of any directed acyclic graph. This ordering is also useful in control flow analysis as it often represents a natural linearization of the control flow. The graph above might represent the flow of control in a code fragment like
if (A) then { B } else { C } D
- and it is natural to consider this code in the order A B C D or A C B D, but not natural to use the order A B D C or A C D B.
Pseudocode
A recursive version of the algorithm:
def dfs(v): mark v as visited preorder-process(v) for all vertices i adjacent to v such that i not visited dfs(i) postorder-process(v)
Another version, without the recursion:
dfs(graph G) { list L = empty tree T = empty choose a starting vertex x search(x) while(L is not empty) { remove edge (v, w) from beginning of L if w not yet visited { add (v, w) to T search(w) } } } search(vertex v) { visit v for each edge (v, w) add edge (v, w) to the beginning of L }
Applications
Here are some algorithms where DFS is used:
- Finding connected components.
- Topological sorting.
- Finding 2-(edge or vertex)-connected components.
- Finding strongly connected components.
- Solving puzzles with only one solution, such as mazes.
External links
- Depth-First Explanation and Example
- C++ Boost Graph Library: Depth-First Search
- Depth-First Search Animation (for a directed graph)
- Depth First and Breadth First Search: Explanation and Code
- dfs applet
References
- Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. Introduction to Algorithms, Second Edition. MIT Press and McGraw-Hill, 2001. ISBN 0-262-03293-7. Section 22.3: Depth-first search, pp.540–549.
- Knuth, Donald E. (1997), The Art Of Computer Programming Vol 1. 3rd ed., Boston: Addison-Wesley, ISBN 0-201-89683-4