Searching Algorithms

Techniques for finding elements in data structures efficiently.

Why Search Matters

Searching is one of the most common operations in programming. Whether you're looking up a user in a database, finding a file on disk, or navigating a mapโ€”searching is everywhere.

๐ŸŽฏ Key Categories

Sequential Search

Linear Search โ€” check every element

Interval Search

Binary Search โ€” divide and conquer

Graph Search

BFS, DFS โ€” traverse connected nodes

Hash-Based

O(1) lookup with hash tables

๐Ÿ“‹ Linear Search

The simplest search: check each element one by one until you find the target. Works on any data โ€” sorted or unsorted.

3
7
2
9
Found!
5
1

โœ… Pros

  • โ€ข Works on unsorted data
  • โ€ข Simple to implement
  • โ€ข O(1) space complexity

โŒ Cons

  • โ€ข O(n) time โ€” slow for large data
  • โ€ข Must check every element
function linearSearch(arr, target) {
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) {
            return i;  // Found at index i
        }
    }
    return -1;  // Not found
}

// Time: O(n) | Space: O(1)

๐Ÿ” Binary Search

Divide and conquer: For sorted arrays, compare the target with the middle element and eliminate half the search space each time.

โšก Key insight: Binary search is O(log n). For 1 billion elements, you only need ~30 comparisons instead of 1 billion!

How it works (finding 23):

Step 1: Check middle
2
5
8
12
16
23
38
56
72
91
16 < 23, search right half
Step 2: Check middle of right half
23
38
56
72
91
56 > 23, search left half
Step 3: Found!
23
38
โœ“ Found 23!
function binarySearch(arr, target) {
    let left = 0;
    let right = arr.length - 1;

    while (left <= right) {
        const mid = Math.floor((left + right) / 2);

        if (arr[mid] === target) {
            return mid;  // Found!
        } else if (arr[mid] < target) {
            left = mid + 1;  // Search right half
        } else {
            right = mid - 1; // Search left half
        }
    }

    return -1;  // Not found
}

// Time: O(log n) | Space: O(1)
// IMPORTANT: Array must be sorted!

โš ๏ธ Prerequisite: Binary search only works on sorted arrays. If your data isn't sorted, sort it first (O(n log n)) or use linear search.

๐ŸŒŠ Breadth-First Search (BFS)

Explore nodes level by level. Visit all neighbors before moving deeper. Uses a queue (FIFO).

Best for: Finding the shortest path in unweighted graphs, level-order traversal of trees.

function bfs(graph, start) {
    const visited = new Set();
    const queue = [start];
    const result = [];

    while (queue.length > 0) {
        const node = queue.shift();  // Dequeue

        if (!visited.has(node)) {
            visited.add(node);
            result.push(node);

            // Add all unvisited neighbors to queue
            for (const neighbor of graph[node]) {
                if (!visited.has(neighbor)) {
                    queue.push(neighbor);
                }
            }
        }
    }

    return result;
}

// Example graph: { A: ['B', 'C'], B: ['D'], C: ['E'], D: [], E: [] }
// BFS from A: ['A', 'B', 'C', 'D', 'E']

๐ŸŠ Depth-First Search (DFS)

Explore as deep as possible before backtracking. Uses a stack (LIFO) or recursion.

Best for: Path finding, cycle detection, topological sorting, solving puzzles (mazes, sudoku).

// Recursive DFS
function dfs(graph, node, visited = new Set()) {
    if (visited.has(node)) return;

    visited.add(node);
    console.log(node);  // Process node

    for (const neighbor of graph[node]) {
        dfs(graph, neighbor, visited);
    }
}

// Iterative DFS (using stack)
function dfsIterative(graph, start) {
    const visited = new Set();
    const stack = [start];

    while (stack.length > 0) {
        const node = stack.pop();  // LIFO

        if (!visited.has(node)) {
            visited.add(node);
            console.log(node);

            // Add neighbors to stack
            for (const neighbor of graph[node]) {
                stack.push(neighbor);
            }
        }
    }
}

BFS vs DFS

AspectBFSDFS
Data StructureQueueStack / Recursion
Time ComplexityO(V + E)O(V + E)
Space ComplexityO(V) โ€” widerO(V) โ€” deeper
Shortest Pathโœ“ Yes (unweighted)โœ— Not guaranteed
Best ForShortest path, level-orderCycle detection, paths

Quick Reference

๐Ÿ”ข
Unsorted array

Use Linear Search O(n)

๐Ÿ“Š
Sorted array

Use Binary Search O(log n)

๐Ÿ—บ๏ธ
Find shortest path in graph

Use BFS

๐Ÿ”„
Detect cycles / explore all paths

Use DFS

โšก
O(1) lookup by key

Use Hash Table

Content adapted from Hello ็ฎ—ๆณ• - Searching by Krahets, licensed under CC BY-NC-SA 4.0.