Search Algorithms in AI: BFS, DFS, A* and How They Work

AI search algorithm visualization showing nodes and paths

Search algorithms are the engine room of artificial intelligence. From GPS navigation finding the fastest route to AlphaGo deciding its next move against a world champion, search is how AI systems explore possibilities and find solutions. Understanding **search algorithms in AI** isn’t just academic—it’s the foundation for building systems that can plan, reason, and solve problems autonomously [citation:1].

This guide breaks down the three most fundamental search strategies: Breadth-First Search (BFS), Depth-First Search (DFS), and A* Search. You’ll learn how each works, when to use which, and why A* has been the gold standard for pathfinding since 1968 [citation:1].

🎯 What you’ll learn: The difference between uninformed and informed search, how BFS and DFS explore state spaces, why A* combines the best of both worlds, and how to choose the right algorithm for your problem.

The Building Blocks: State Spaces and Search Problems

Before diving into algorithms, you need to understand what they’re searching through. Every search problem can be modeled with four components [citation:1]:

  • State Space: All possible configurations the problem can be in. For a GPS, this is every possible location on the map. For a puzzle, every possible arrangement of pieces.
  • Initial State: Where the search begins—your starting location or the puzzle’s starting arrangement.
  • Goal State: The target configuration—your destination or the solved puzzle.
  • Actions and Transition Model: What moves are possible from any given state, and what new state results from taking an action.

Search algorithms explore this state space by building a tree of possibilities. The root is the initial state, branches are actions, and nodes are the resulting states. The algorithm’s job is to find a path from root to a goal state—ideally the shortest or cheapest path [citation:5].

Uninformed vs. Informed Search: Know the Difference

AI search algorithms fall into two broad categories [citation:2]:

Uninformed (Blind) Search Informed (Heuristic) Search
Explores using only problem definition—no “hints” about where the goal might be Uses domain-specific knowledge (heuristics) to estimate which paths are most promising
BFS, DFS, Uniform-Cost Search A*, Greedy Best-First Search
Systematic but can be inefficient on large problems More efficient when good heuristics are available
Example: Exploring a maze with your eyes closed Example: Exploring a maze while seeing which direction the exit lies
Code editor showing search algorithm implementation

Breadth-First Search (BFS): Explore Level by Level

Uninformed Queue-Based Complete Optimal (Unweighted)

What it does: BFS explores the state space layer by layer—first all states one step from start, then all states two steps away, and so on. It uses a queue (FIFO) to manage the frontier of nodes to explore [citation:1].

How it works: Start with the initial state in the queue. While the queue isn’t empty, remove the front node. If it’s the goal, you’re done. Otherwise, add all its unvisited neighbors to the back of the queue and continue [citation:3].

from collections import deque

def bfs(start, is_goal, get_neighbors):
    visited = set([start])
    queue = deque([start])
    
    while queue:
        current = queue.popleft()
        if is_goal(current):
            return current  # Solution found
        
        for neighbor in get_neighbors(current):
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)
    return None  # No solution

When BFS shines:

  • Finding the shortest path in an unweighted graph (e.g., minimum number of moves in a puzzle) [citation:5]
  • Web crawling (visiting pages level by level)
  • Social network analysis (“degrees of separation”)
  • Any problem where all actions have equal cost

The tradeoff: BFS guarantees the shortest path in terms of number of steps, but it consumes significant memory—O(b^d) where b is the branching factor and d is the solution depth. For a problem with 10 possible moves per state and a solution 8 steps deep, BFS might store millions of nodes [citation:5].

Real-world example: In the 8-puzzle problem, BFS systematically explores all configurations one move away, then two moves away, eventually finding the optimal 25-move solution after examining 145,605 states [citation:1].

Depth-First Search (DFS): Go Deep Before Wide

Uninformed Stack-Based Complete* Not Optimal

What it does: DFS plunges as far down a single path as possible before backtracking. It uses a stack (LIFO)—either explicitly or through recursion—to track the current exploration path [citation:1].

How it works: Start at the initial state. Pick one neighbor and recursively explore everything reachable from there before considering any other neighbors. When you hit a dead end, backtrack to the most recent unexplored branch [citation:5].

def dfs(node, is_goal, get_neighbors, visited=None):
    if visited is None:
        visited = set()
    
    visited.add(node)
    if is_goal(node):
        return node  # Solution found
    
    for neighbor in get_neighbors(node):
        if neighbor not in visited:
            result = dfs(neighbor, is_goal, get_neighbors, visited)
            if result:
                return result
    return None  # No solution in this branch

When DFS shines:

  • Memory-constrained environments (DFS only stores the current path, O(bm) space vs BFS’s O(b^d)) [citation:5]
  • Problems where any solution is acceptable, not necessarily the shortest
  • Topological sorting, cycle detection, and maze generation
  • Puzzle solving where the state space is deep but solutions exist at various depths

The tradeoff: DFS can get lost in infinite branches if the state space is unbounded. Even in finite spaces, it may find a long, meandering solution while missing a much shorter one. In the 8-puzzle, DFS once found a 1,157-move “solution” when an optimal 25-move path existed [citation:1].

Critical note: DFS is only complete if the state space is finite and you avoid revisiting states. Without cycle detection, DFS can loop forever [citation:5].

A* Search: The Best of Both Worlds

Informed Priority Queue Complete Optimal

What it does: A* combines the systematic exploration of BFS with heuristic guidance about which direction leads to the goal. It evaluates nodes using f(n) = g(n) + h(n), where g(n) is the cost from start to current node, and h(n) is an estimate of the cost from current node to goal [citation:3].

How it works: Maintain a priority queue ordered by f(n). At each step, expand the node with the lowest f-value. This balances two competing desires: exploring promising paths (low h) while not abandoning efficient paths (low g) [citation:8].

import heapq

def a_star(start, is_goal, get_neighbors, cost_func, heuristic):
    # Priority queue stores (f_score, counter, node) to break ties
    counter = 0
    open_set = [(0, counter, start)]
    heapq.heapify(open_set)
    
    g_score = {start: 0}
    visited = set()
    
    while open_set:
        f, _, current = heapq.heappop(open_set)
        if current in visited:
            continue
            
        visited.add(current)
        if is_goal(current):
            return current
            
        for neighbor in get_neighbors(current):
            tentative_g = g_score[current] + cost_func(current, neighbor)
            if neighbor not in g_score or tentative_g < g_score[neighbor]:
                g_score[neighbor] = tentative_g
                f_score = tentative_g + heuristic(neighbor)
                counter += 1
                heapq.heappush(open_set, (f_score, counter, neighbor))
    return None

The magic is in the heuristic: The heuristic function h(n) estimates the remaining cost. For A* to be optimal, the heuristic must be admissible—it must never overestimate the true cost. For grid-based pathfinding, Manhattan distance (|x1-x2| + |y1-y2|) is a classic admissible heuristic [citation:7].

When A* dominates:

  • GPS and navigation systems (finding shortest driving routes)
  • Video game AI (pathfinding for characters avoiding obstacles) [citation:9]
  • Robotics (autonomous navigation on 2D grids) [citation:3]
  • Puzzle solving where optimal solutions matter
  • Any problem where you can define a reasonable heuristic

Why it's the gold standard: A* expands significantly fewer nodes than BFS while still guaranteeing optimal solutions. In the 8-puzzle with Manhattan distance heuristic, A* expands only a fraction of the nodes BFS does to find the same optimal 25-move solution [citation:7].

BFS vs. DFS vs. A*: Side-by-Side Comparison

Here's how the three algorithms stack up on the metrics that matter for real applications [citation:5]:

Algorithm Complete? Optimal? Time Space Best For
BFS ✅ Yes ✅ Yes (unweighted) O(b^d) O(b^d) Shallow goals, uniform costs
DFS ✅ Yes* ❌ No O(b^m) O(bm) Deep search, memory constraints
A* ✅ Yes ✅ Yes** O(b^d) O(b^d) Pathfinding with good heuristics

*DFS is complete only in finite state spaces with cycle detection. **A* is optimal when using an admissible heuristic. b = branching factor, d = solution depth, m = maximum depth.

Essential A* Concepts: Admissibility and Consistency

A*'s guarantees of optimality depend on two properties of the heuristic function [citation:5]:

  • Admissible Heuristic: A heuristic h(n) is admissible if it never overestimates the true cost to reach the goal. For any node n, h(n) ≤ h*(n) where h*(n) is the actual optimal cost. Manhattan distance is admissible for grid movement because you can't reach the goal in fewer steps than the Manhattan distance.
  • Consistent (Monotonic) Heuristic: A heuristic is consistent if for every node n and successor n', h(n) ≤ c(n, n') + h(n'). This ensures f-values never decrease along a path. Consistency implies admissibility and enables more efficient implementations.
🔑 The key insight: A* with an admissible heuristic is guaranteed to find the optimal solution. With an inconsistent but admissible heuristic, it may re-expand nodes but still finds optimal paths. With a poor heuristic (e.g., h(n) = 0), A* degenerates to Uniform-Cost Search [citation:5].

Beyond Classical Search: The AI Evolution

While BFS, DFS, and A* form the foundation, modern AI has extended search in powerful directions [citation:1]:

  • Monte Carlo Tree Search (MCTS): Used by AlphaGo and AlphaZero, MCTS combines random sampling with tree search to handle enormous state spaces like Go. It's why AI can now defeat world champions [citation:1].
  • Population-Based Algorithms: Genetic algorithms and particle swarm optimization explore multiple solution candidates simultaneously, useful when the search space is too large for systematic exploration [citation:1].
  • Deep Reinforcement Learning: DQN and its successors learn to search implicitly through neural networks, handling raw sensory input like pixels from Atari games [citation:1].

Yet even with these advances, BFS, DFS, and A* remain the building blocks. Understanding them gives you the intuition to grasp why MCTS works and when to apply which technique.

How to Choose: A Practical Decision Tree

  1. Do all actions have the same cost?
    • Yes, and you need the shortest path → BFS
    • Yes, but memory is tight and any solution works → DFS
  2. Do actions have different costs?
    • No good heuristic available → Uniform-Cost Search
    • Good heuristic available → A*
  3. Is the state space massive or infinite?
    • Consider local search (Hill Climbing, Simulated Annealing) or MCTS
  4. Is this a two-player competitive game?
    • Use Minimax with Alpha-Beta Pruning [citation:5]

Search Is Fundamental

Search algorithms in AI aren't just interview questions—they're how intelligent systems make decisions. Whether you're building a game, a navigation app, or a puzzle solver, BFS, DFS, and A* are the tools you'll reach for. BFS guarantees the shortest path but consumes memory. DFS sips memory but may wander. A* combines the best of both when you have a good heuristic.

The best way to internalize these algorithms? Implement them. Build a simple grid pathfinder with obstacles. Watch BFS expand in concentric circles. See DFS snake down a single corridor. Observe how A*'s expansion contour bends toward the goal. That visual intuition will serve you long after the pseudocode fades.

Practice with Python → Uninformed Search Deep Dive →

Scroll to Top