top of page

Search Algorithms: A Refresher

  • mosesg1123
  • Mar 28
  • 4 min read

Updated: 6 days ago

Searching is a fundamental operation in computer science, and understanding search algorithms is essential for coding interviews. Whether you're a new engineer brushing up on the basics or an experienced developer looking for a quick refresher, this guide covers the most important search algorithms, their time complexities, and when to use each.


1. Linear Search (Brute-Force Search)

Overview

Linear search is the simplest search algorithm. It sequentially checks each element in an array or list until it finds the target value or reaches the end.


Time Complexity

  • Best case: O(1) (if the target is the first element)

  • Worst case: O(n) (if the target is at the end or not present)

  • Average case: O(n)


Use Cases

✔️ Small datasets

✔️ Unsorted lists or arrays

✔️ When searching for multiple occurrences of a value


Implementation (Python)

def linear_search(arr, target):
    for i in range(len(arr)):
        if arr[i] == target:
            return i  # Return index of the found element
    return -1  # Target not found

# Example
arr = [3, 5, 2, 9, 4]
print(linear_search(arr, 9))  # Output: 3

2. Binary Search (Divide and Conquer)

Overview

Binary search is much faster than linear search but requires the input to be sorted. It repeatedly divides the search space in half until it finds the target.


Time Complexity

  • Best case: O(1) (if the middle element is the target)

  • Worst case: O(log n)

  • Average case: O(log n)


Use Cases

✔️ Large sorted datasets

✔️ Searching in sorted arrays, binary trees, or dictionaries

✔️ Efficient for range queries


Implementation (Iterative Approach)

def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1  # Target not found

# Example
arr = [1, 3, 5, 7, 9, 11]
print(binary_search(arr, 7))  # Output: 3

Implementation (Recursive Approach)

def binary_search_recursive(arr, target, left, right):
    if left > right:
        return -1  # Base case: target not found
    
    mid = left + (right - left) // 2
    if arr[mid] == target:
        return mid
    elif arr[mid] < target:
        return binary_search_recursive(arr, target, mid + 1, right)
    else:
        return binary_search_recursive(arr, target, left, mid - 1)

# Example
arr = [1, 3, 5, 7, 9, 11]
print(binary_search_recursive(arr, 5, 0, len(arr) - 1))  # Output: 2

3. Jump Search (Block Search)

Overview

Jump search is an optimization over linear search for sorted arrays. It skips ahead by a fixed block size (√n) and then performs a linear search within the block where the target might exist.


Time Complexity

  • Best case: O(1)

  • Worst case: O(√n)

  • Average case: O(√n)


Use Cases

✔️ Searching in large sorted datasets where binary search is not ideal

✔️ Scenarios where jumping over blocks is faster than comparing every element


Implementation

import math

def jump_search(arr, target):
    n = len(arr)
    step = int(math.sqrt(n))  # Block size
    prev = 0

    while prev < n and arr[min(step, n) - 1] < target:
        prev = step
        step += int(math.sqrt(n))
        if prev >= n:
            return -1  # Target not found

    for i in range(prev, min(step, n)):  # Linear search within the block
        if arr[i] == target:
            return i
    return -1

# Example
arr = [1, 3, 5, 7, 9, 11, 13, 15]
print(jump_search(arr, 7))  # Output: 3

4. Interpolation Search (Improved Binary Search)

Overview

Interpolation search is an improvement over binary search for uniformly distributed sorted data. Instead of jumping to the middle, it estimates the probable position of the target using a formula.


Time Complexity

  • Best case: O(1)

  • Worst case: O(n) (if distribution is skewed)

  • Average case: O(log log n)


Use Cases

✔️ Large datasets with uniformly distributed values

✔️ Efficient for searching in databases and large sorted arrays


Implementation

def interpolation_search(arr, target):
    low, high = 0, len(arr) - 1

    while low <= high and arr[low] <= target <= arr[high]:
        if low == high:
            return low if arr[low] == target else -1

        pos = low + ((target - arr[low]) * (high - low) // (arr[high] - arr[low]))

        if arr[pos] == target:
            return pos
        elif arr[pos] < target:
            low = pos + 1
        else:
            high = pos - 1

    return -1  # Target not found

# Example
arr = [10, 20, 30, 40, 50, 60, 70]
print(interpolation_search(arr, 40))  # Output: 3

5. Exponential Search (For Unbounded or Large Arrays)

Overview

Exponential search is useful when the size of the array is unknown or very large. It works by doubling the search range (exponentially increasing steps) until the target is within range, then performing binary search.


Time Complexity

  • Best case: O(1)

  • Worst case: O(log n)

  • Average case: O(log n)


Use Cases

✔️ Searching in unbounded lists or infinite-sized arrays

✔️ Large sorted datasets where standard binary search isn't feasible


Implementation

def exponential_search(arr, target):
    if arr[0] == target:
        return 0
    
    index = 1
    while index < len(arr) and arr[index] <= target:
        index *= 2

    return binary_search(arr[:min(index, len(arr))], target)

# Example
arr = [1, 2, 4, 8, 16, 32, 64, 128]
print(exponential_search(arr, 16))  # Output: 4

Summary Table

Algorithm

Best Case

Worst Case

Average Case

When to Use?

Linear Search

O(1)

O(n)

O(n)

Unsorted small datasets

Binary Search

O(1)

O(log n)

O(log n)

Sorted arrays/lists

Jump Search

O(1)

O(√n)

O(√n)

Large sorted arrays

Interpolation Search

O(1)

O(n)

O(log log n)

Uniformly distributed sorted data

Exponential Search

O(1)

O(log n)

O(log n)

Large unknown/unbounded datasets

Mastering these search algorithms will help you ace your technical interviews. Happy coding! 🚀

Recent Posts

See All
Missing Number

When you need to find the one missing entry in a sequence—say missing log IDs, seats, or data packets— Missing Number  is the classic...

 
 
 
Top K Frequent Elements

Finding the most frequent items in a dataset is a common real-world task—think trending topics in social media, most visited pages on a...

 
 
 
Intersection of Two Arrays

Finding the intersection of two arrays - i.e. the common elements between two lists - is a real-world task you’ll do when reconciling...

 
 
 

Comments


Drop Me a Line, Let Me Know What You Think

Thanks for submitting!

© 2023 by Train of Thoughts. Proudly created with Wix.com

bottom of page