Skillversitas

Depth-First Search

Depth-First Search (DFS) is a traversal algorithm that explores as far as possible down one branch before backtracking. It uses a Stack or recursion.

Concept

Graph:     A --- B
           |     |
           C --- D

DFS from A: A -> B -> D -> C (depth first)

Recursive Implementation

def dfs_recursive(graph, vertex, visited=None):
    if visited is None:
        visited = set()

    visited.add(vertex)
    order = [vertex]

    for neighbor in graph[vertex]:
        if neighbor not in visited:
            order.extend(dfs_recursive(graph, neighbor, visited))

    return order

graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D'],
    'C': ['A', 'D'],
    'D': ['B', 'C']
}

print(dfs_recursive(graph, 'A'))  # ['A', 'B', 'D', 'C']

Iterative Implementation (Stack)

def dfs_iterative(graph, start):
    visited = set()
    stack = [start]
    order = []

    while stack:
        vertex = stack.pop()
        if vertex not in visited:
            visited.add(vertex)
            order.append(vertex)
            # Reverse so the left neighbor is processed first
            for neighbor in reversed(graph[vertex]):
                if neighbor not in visited:
                    stack.append(neighbor)

    return order

print(dfs_iterative(graph, 'A'))  # ['A', 'B', 'D', 'C']

DFS for Binary Trees

Preorder (Root, Left, Right)

def preorder(node):
    if not node:
        return []
    return [node.val] + preorder(node.left) + preorder(node.right)

Inorder (Left, Root, Right)

def inorder(node):
    if not node:
        return []
    return inorder(node.left) + [node.val] + inorder(node.right)

Postorder (Left, Right, Root)

def postorder(node):
    if not node:
        return []
    return postorder(node.left) + postorder(node.right) + [node.val]

Cycle Detection

def has_cycle(graph):
    visited = set()
    rec_stack = set()

    def dfs(vertex):
        visited.add(vertex)
        rec_stack.add(vertex)

        for neighbor in graph[vertex]:
            if neighbor not in visited:
                if dfs(neighbor):
                    return True
            elif neighbor in rec_stack:
                return True

        rec_stack.remove(vertex)
        return False

    for vertex in graph:
        if vertex not in visited:
            if dfs(vertex):
                return True
    return False

Topological Sort

For a Directed Acyclic Graph (DAG):

def topological_sort(graph):
    visited = set()
    stack = []

    def dfs(vertex):
        visited.add(vertex)
        for neighbor in graph[vertex]:
            if neighbor not in visited:
                dfs(neighbor)
        stack.append(vertex)

    for vertex in graph:
        if vertex not in visited:
            dfs(vertex)

    return stack[::-1]

# Example dependency graph
dep_graph = {
    'A': ['B', 'C'],
    'B': ['D'],
    'C': ['D'],
    'D': []
}

print(topological_sort(dep_graph))  # ['A', 'C', 'B', 'D'] or similar

Time Complexity

Representation Time
Adjacency List O(V + E)
Adjacency Matrix O(V²)

DFS vs BFS

Aspect DFS BFS
Structure Stack/Recursion Queue
Depth Explores deep first Level by level
Shortest path No Yes (unweighted)
Memory O(height) O(width)
Use case Cycle detection, topological sort Shortest path, level traversal

DFS is powerful for recursive traversal. Now let's study sorting algorithms.