algorithm
depth-first-search
non-recursive
graph-traversal
programming

Non-recursive depth first search algorithm

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction to Non-recursive Depth First Search (DFS) Algorithm

Depth First Search (DFS) is a fundamental graph traversal technique influential in both theoretical and practical aspects of computer science. Typically, DFS uses recursion, which leverages the function call stack. Non-recursive DFS, however, implements the DFS logic iteratively using an explicit stack data structure, thereby avoiding the limitations of recursion, such as stack overflow for deep recursions.

Understanding Graphs

Before delving into DFS, it's essential to understand graphs:

  • Graph: A collection of nodes (vertices) and edges (connections between nodes).
  • Undirected Graph: Edges lack direction; (A, B) implies (B, A).
  • Directed Graph: Edges have direction; (A, B) does not imply (B, A).
  • Cyclic Graphs: Graphs containing cycles.
  • Acyclic Graphs: Graphs without any cycles.

Graphs can be represented using adjacency lists or matrices. The choice of representation affects the DFS implementation.

Algorithm of Non-recursive DFS

The primary mechanism to solve non-recursive DFS involves using an explicit stack to simulate the call stack of recursion.

Steps of Non-recursive DFS

  1. Initialization:
    • Start by selecting a node (arbitrary or specified as the root node).
    • Initialize an empty stack and push the starting node onto it.
    • Maintain a set (or similar data structure) to record visited nodes.
  2. Traversal:
    • While the stack is not empty:
      • Pop the top node from the stack and check if it is visited.
      • If not visited, mark as visited and process it.
      • Push all adjacent unvisited nodes into the stack.
  3. Termination:
    • The algorithm terminates when the stack becomes empty, indicating all reachable nodes are visited.

Pseudocode Example

plaintext
1function DFS(graph, start_vertex):
2    stack = [start_vertex]        // Initialize the stack with the starting vertex
3    visited = set()               // Empty set to keep track of visited vertices
4
5    while stack is not empty:     // Continue until the stack is empty
6        vertex = stack.pop()      // Take the next vertex from the top of the stack
7
8        if vertex not in visited: // If the vertex has not been visited yet
9            visit(vertex)         // Process the vertex
10            visited.add(vertex)   // Add it to the visited set
11
12            // Add adjacent unvisited vertices to the stack.
13            for each adjacent_vertex in graph.adjacency_list[vertex]:
14                if adjacent_vertex not in visited:
15                    stack.push(adjacent_vertex)

Practical Example

Consider a simple undirected graph:

 
1       A
2      / \
3     B   C
4    /|   |\
5   D E   F G

Starting DFS from vertex A using a non-recursive approach would look like this:

  1. Initial stack: [A]
  2. Pop A, visit, and push unvisited neighbors [B, C].
  3. Pop C, visit, and push neighbors [B, F, G].
  4. Continue this process until the stack is empty.

Advantages of Non-recursive DFS

  • Space Efficiency: Reduces the risk of stack overflow prevalent in recursive DFS for deep graphs.
  • Control: Offers explicit control over the stack, beneficial for iterative manipulation and debugging.

Comparison Table

AspectRecursive DFSNon-recursive DFS
ImplementationSimpler due to implicit stackSlightly more complex due to manual stack usage
Space ComplexityCan be high due to call stackGenerally lower, depends on stack
LimitationsStack overflow in deeply nested callsLimited by available system memory for the stack
PerformanceBoth are usually O(V+E)O(V + E) (vertices plus edges)Same as recursive DFS

Additional Details

Use Cases for Non-recursive DFS

  • Backtracking: Often used in puzzles like mazes.
  • Topological Sorting: In directed acyclic graphs.
  • Cycle Detection: In directed and undirected graphs.
  • Path Finding: Finding paths between nodes.

Optimizations

  • For large graphs, minimize memory usage by using data structures like bloom filters for visited checks.
  • Leverage bit manipulation for state representation in dense graphs.

Conclusion

Non-recursive DFS is an efficient and versatile algorithm to traverse and explore graphs. Its iterative nature avoids issues associated with recursion while maintaining the depth exploration philosophy intrinsic to DFS. Whether you're engineering a complex graph-based system or exploring theoretic computer science, understanding both recursive and non-recursive DFS implementations is indispensable.


Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.