Bird
Raised Fist0
Interview Prepmodified-binary-searcheasyAmazonGoogleFacebookMicrosoft

Binary Search

Choose your preparation mode4 modes available
🎯
Binary Search
easyBINARY_SEARCHAmazonGoogleFacebook

Imagine searching for a specific book in a sorted library shelf. Instead of checking every book, you cleverly split the shelf in half repeatedly to find your book faster.

💡 Binary Search is a fundamental algorithm used to efficiently find an element in a sorted array. Beginners often struggle with correctly managing the search boundaries and termination conditions, which can lead to infinite loops or missed elements.
📋
Problem Statement

Given a sorted array of integers nums and an integer target, return the index of target if it exists in nums. Otherwise, return -1. You must write an algorithm with O(log n) runtime complexity.

1 ≤ nums.length ≤ 10^5-10^9 ≤ nums[i], target ≤ 10^9nums is sorted in ascending order
💡
Example
Input"nums = [-1,0,3,5,9,12], target = 9"
Output4

9 is found at index 4 in the array.

Input"nums = [-1,0,3,5,9,12], target = 2"
Output-1

2 is not present in the array, so return -1.

  • Empty array → return -1
  • Target smaller than all elements → return -1
  • Target larger than all elements → return -1
  • Array with one element equal to target → return 0
⚠️
Common Mistakes
Using mid = (low + high) / 2 without overflow protection

Integer overflow on large arrays causing wrong mid calculation

✅ Use mid = low + (high - low) // 2 to avoid overflow

Using while (low < high) instead of while (low <= high)

May miss checking the last element, causing incorrect -1 return

✅ Use while (low <= high) to ensure all elements are checked

Updating low = mid instead of low = mid + 1

Infinite loop when low equals mid repeatedly

✅ Increment low to mid + 1 to shrink search space

Not handling empty array input

Index errors or incorrect output

✅ Check if array is empty before searching or handle gracefully

🧠
Brute Force (Linear Search)
💡 This approach exists to establish a baseline solution that is easy to understand but inefficient. It helps beginners grasp the problem before optimizing.

Intuition

Check each element one by one until the target is found or the array ends.

Algorithm

  1. Start from the first element of the array.
  2. Compare the current element with the target.
  3. If they match, return the current index.
  4. If the end of the array is reached without a match, return -1.
💡 This algorithm is straightforward but inefficient for large arrays because it checks every element.
</>
Code
def linear_search(nums, target):
    for i in range(len(nums)):
        if nums[i] == target:
            return i
    return -1

# Driver code
if __name__ == '__main__':
    nums = [-1,0,3,5,9,12]
    target = 9
    print(linear_search(nums, target))  # Output: 4
Line Notes
for i in range(len(nums))Iterate over each index to check every element
if nums[i] == targetCheck if current element matches the target
return iReturn index immediately upon finding the target
return -1Return -1 if target is not found after full scan
public class LinearSearch {
    public static int linearSearch(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == target) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] nums = {-1,0,3,5,9,12};
        int target = 9;
        System.out.println(linearSearch(nums, target)); // Output: 4
    }
}
Line Notes
for (int i = 0; i < nums.length; i++)Loop through each element in the array
if (nums[i] == target)Check if current element equals target
return iReturn index when target is found
return -1Return -1 if target is not found after loop
#include <iostream>
#include <vector>
using namespace std;

int linearSearch(const vector<int>& nums, int target) {
    for (int i = 0; i < nums.size(); i++) {
        if (nums[i] == target) {
            return i;
        }
    }
    return -1;
}

int main() {
    vector<int> nums = {-1,0,3,5,9,12};
    int target = 9;
    cout << linearSearch(nums, target) << endl; // Output: 4
    return 0;
}
Line Notes
for (int i = 0; i < nums.size(); i++)Iterate over all elements in the vector
if (nums[i] == target)Check if current element matches target
return iReturn index immediately when found
return -1Return -1 if target not found after full iteration
function linearSearch(nums, target) {
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] === target) {
            return i;
        }
    }
    return -1;
}

// Driver code
const nums = [-1,0,3,5,9,12];
const target = 9;
console.log(linearSearch(nums, target)); // Output: 4
Line Notes
for (let i = 0; i < nums.length; i++)Loop through each element in the array
if (nums[i] === target)Check if current element equals target
return iReturn index immediately when target is found
return -1Return -1 if target is not found after full loop
⏱
Complexity
TimeO(n)
SpaceO(1)

We potentially check every element once, so time is linear in array size. Space is constant as no extra storage is used.

💡 For n=100, this means up to 100 comparisons, which is inefficient compared to binary search.
Interview Verdict: Accepted but inefficient for large inputs

This approach works but is too slow for large arrays, motivating the need for binary search.

🧠
Binary Search (Iterative)
💡 This approach introduces the classic binary search algorithm that efficiently halves the search space each iteration, drastically improving performance.

Intuition

Repeatedly divide the search interval in half by comparing the middle element to the target, narrowing down where the target can be.

Algorithm

  1. Initialize low and high pointers to start and end of array.
  2. While low is less than or equal to high:
  3. Calculate mid as the average of low and high.
  4. If nums[mid] equals target, return mid.
  5. If nums[mid] is less than target, move low to mid + 1.
  6. Else, move high to mid - 1.
  7. If loop ends without finding target, return -1.
💡 The key challenge is correctly updating low and high to avoid infinite loops and ensure all elements are checked.
</>
Code
def binary_search(nums, target):
    low, high = 0, len(nums) - 1
    while low <= high:
        mid = low + (high - low) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

# Driver code
if __name__ == '__main__':
    nums = [-1,0,3,5,9,12]
    target = 9
    print(binary_search(nums, target))  # Output: 4
Line Notes
low, high = 0, len(nums) - 1Initialize search boundaries to cover entire array
while low <= highContinue searching while there is a valid range
mid = low + (high - low) // 2Calculate middle index safely to avoid overflow
if nums[mid] == targetCheck if middle element is the target
public class BinarySearch {
    public static int binarySearch(int[] nums, int target) {
        int low = 0, high = nums.length - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] nums = {-1,0,3,5,9,12};
        int target = 9;
        System.out.println(binarySearch(nums, target)); // Output: 4
    }
}
Line Notes
int low = 0, high = nums.length - 1Set initial search boundaries
while (low <= high)Loop while search space is valid
int mid = low + (high - low) / 2Calculate mid index safely
if (nums[mid] == target)Check if mid element matches target
#include <iostream>
#include <vector>
using namespace std;

int binarySearch(const vector<int>& nums, int target) {
    int low = 0, high = nums.size() - 1;
    while (low <= high) {
        int mid = low + (high - low) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    return -1;
}

int main() {
    vector<int> nums = {-1,0,3,5,9,12};
    int target = 9;
    cout << binarySearch(nums, target) << endl; // Output: 4
    return 0;
}
Line Notes
int low = 0, high = nums.size() - 1Initialize search boundaries
while (low <= high)Continue while search space is valid
int mid = low + (high - low) / 2Calculate mid index to avoid overflow
if (nums[mid] == target)Check if mid element is the target
function binarySearch(nums, target) {
    let low = 0, high = nums.length - 1;
    while (low <= high) {
        let mid = low + Math.floor((high - low) / 2);
        if (nums[mid] === target) {
            return mid;
        } else if (nums[mid] < target) {
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    return -1;
}

// Driver code
const nums = [-1,0,3,5,9,12];
const target = 9;
console.log(binarySearch(nums, target)); // Output: 4
Line Notes
let low = 0, high = nums.length - 1Initialize search boundaries
while (low <= high)Loop while search space is valid
let mid = low + Math.floor((high - low) / 2)Calculate mid index safely
if (nums[mid] === target)Check if mid element matches target
⏱
Complexity
TimeO(log n)
SpaceO(1)

Each iteration halves the search space, so the number of steps grows logarithmically with input size. Space is constant as no extra storage is used.

💡 For n=1,000,000, this means about 20 comparisons, which is much faster than linear search.
Interview Verdict: Accepted and optimal for sorted arrays

This is the standard approach expected in interviews for searching in sorted arrays.

🧠
Binary Search (Recursive)
💡 This approach uses recursion to implement binary search, which can be more intuitive for some but risks stack overflow on large inputs.

Intuition

Recursively narrow down the search space by comparing the middle element to the target and searching the appropriate half.

Algorithm

  1. Define a recursive function with low and high indices.
  2. If low is greater than high, return -1 (target not found).
  3. Calculate mid as the average of low and high.
  4. If nums[mid] equals target, return mid.
  5. If nums[mid] is less than target, recurse on right half (mid + 1 to high).
  6. Else, recurse on left half (low to mid - 1).
💡 The recursion must have a clear base case to avoid infinite calls and stack overflow.
</>
Code
def binary_search_recursive(nums, target, low, high):
    if low > high:
        return -1
    mid = low + (high - low) // 2
    if nums[mid] == target:
        return mid
    elif nums[mid] < target:
        return binary_search_recursive(nums, target, mid + 1, high)
    else:
        return binary_search_recursive(nums, target, low, mid - 1)

# Driver code
if __name__ == '__main__':
    nums = [-1,0,3,5,9,12]
    target = 9
    print(binary_search_recursive(nums, target, 0, len(nums) - 1))  # Output: 4
Line Notes
if low > highBase case: no valid search space left
mid = low + (high - low) // 2Calculate middle index safely
if nums[mid] == targetCheck if mid element is target
return binary_search_recursive(nums, target, mid + 1, high)Recurse on right half if target is greater
public class BinarySearchRecursive {
    public static int binarySearchRecursive(int[] nums, int target, int low, int high) {
        if (low > high) {
            return -1;
        }
        int mid = low + (high - low) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            return binarySearchRecursive(nums, target, mid + 1, high);
        } else {
            return binarySearchRecursive(nums, target, low, mid - 1);
        }
    }

    public static void main(String[] args) {
        int[] nums = {-1,0,3,5,9,12};
        int target = 9;
        System.out.println(binarySearchRecursive(nums, target, 0, nums.length - 1)); // Output: 4
    }
}
Line Notes
if (low > high)Base case: search space invalid
int mid = low + (high - low) / 2Calculate mid index safely
if (nums[mid] == target)Check if mid element matches target
return binarySearchRecursive(nums, target, mid + 1, high)Recurse on right half if target is greater
#include <iostream>
#include <vector>
using namespace std;

int binarySearchRecursive(const vector<int>& nums, int target, int low, int high) {
    if (low > high) {
        return -1;
    }
    int mid = low + (high - low) / 2;
    if (nums[mid] == target) {
        return mid;
    } else if (nums[mid] < target) {
        return binarySearchRecursive(nums, target, mid + 1, high);
    } else {
        return binarySearchRecursive(nums, target, low, mid - 1);
    }
}

int main() {
    vector<int> nums = {-1,0,3,5,9,12};
    int target = 9;
    cout << binarySearchRecursive(nums, target, 0, nums.size() - 1) << endl; // Output: 4
    return 0;
}
Line Notes
if (low > high)Base case: no valid search space
int mid = low + (high - low) / 2Calculate mid index safely
if (nums[mid] == target)Check if mid element equals target
return binarySearchRecursive(nums, target, mid + 1, high)Recurse on right half if target is greater
function binarySearchRecursive(nums, target, low, high) {
    if (low > high) {
        return -1;
    }
    let mid = low + Math.floor((high - low) / 2);
    if (nums[mid] === target) {
        return mid;
    } else if (nums[mid] < target) {
        return binarySearchRecursive(nums, target, mid + 1, high);
    } else {
        return binarySearchRecursive(nums, target, low, mid - 1);
    }
}

// Driver code
const nums = [-1,0,3,5,9,12];
const target = 9;
console.log(binarySearchRecursive(nums, target, 0, nums.length - 1)); // Output: 4
Line Notes
if (low > high)Base case to stop recursion when search space is empty
let mid = low + Math.floor((high - low) / 2)Calculate mid index safely
if (nums[mid] === target)Check if mid element matches target
return binarySearchRecursive(nums, target, mid + 1, high)Recurse on right half if target is greater
⏱
Complexity
TimeO(log n)
SpaceO(log n)

Time complexity is logarithmic due to halving search space each call. Space complexity is logarithmic due to recursion call stack.

💡 For large n, recursion depth can be about 20 calls, which uses more memory than iterative approach.
Interview Verdict: Accepted but less space efficient than iterative

Recursive binary search is elegant but can cause stack overflow on very large inputs; iterative is preferred in interviews.

📊
All Approaches - One-Glance Tradeoffs
💡 Iterative binary search is the best choice for interviews due to optimal time, space, and no recursion risk.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(n)O(1)NoN/AMention only - never code
2. Binary Search IterativeO(log n)O(1)NoN/ACode this approach
3. Binary Search RecursiveO(log n)O(log n)Yes (deep recursion)N/AMention as alternative, rarely code
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, practice coding all approaches, and prepare to explain tradeoffs clearly in interviews.

How to Present

Step 1: Clarify input constraints and confirm array is sorted.Step 2: State brute force linear search to establish correctness.Step 3: Introduce binary search iterative approach for efficiency.Step 4: Optionally mention recursive binary search and tradeoffs.Step 5: Code the iterative binary search carefully handling boundaries.Step 6: Test with edge cases and explain time/space complexity.

Time Allocation

Clarify: 2min → Approach: 3min → Code: 8min → Test: 2min. Total ~15min

What the Interviewer Tests

Interviewer tests understanding of binary search logic, boundary conditions, termination, and ability to write bug-free code.

Common Follow-ups

  • What if the array is rotated? → Use modified binary search.
  • How to find first or last occurrence of duplicates? → Adjust boundary updates.
💡 These follow-ups test your ability to adapt binary search to variations and edge cases.
🔍
Pattern Recognition

When to Use

1) Input is sorted or can be sorted, 2) Need to find element or boundary efficiently, 3) Problem hints at halving search space, 4) Requires O(log n) or better time.

Signature Phrases

sorted arrayfind targetlogarithmic timesearch efficiently

NOT This Pattern When

Linear search or unordered search problems that do not leverage sorted input

Similar Problems

Find First and Last Position of Element in Sorted Array - binary search variant for duplicatesSearch Insert Position - binary search to find insertion index

Practice

(1/5)
1. Consider the following Python function that finds the kth positive missing number from a sorted array arr. What is the returned value when calling findKthPositive([2, 4, 7], 3)?
def findKthPositive(arr, k):
    n = len(arr)
    total_missing = arr[-1] - n
    if k > total_missing:
        return arr[-1] + (k - total_missing)
    left, right = 0, n - 1
    while left <= right:
        mid = (left + right) // 2
        missing = arr[mid] - (mid + 1)
        if missing < k:
            left = mid + 1
        else:
            right = mid - 1
    return arr[right] + (k - (arr[right] - (right + 1)))
easy
A. 5
B. 8
C. 9
D. 7

Solution

  1. Step 1: Calculate total missing numbers

    Array length n=3, arr[-1]=7, total_missing = 7 - 3 = 4. Since k=3 ≤ 4, proceed with binary search.
  2. Step 2: Perform binary search iterations

    Initial left=0, right=2. - mid=1, missing=arr[1] - (1+1) = 4 - 2 = 2 < 3 -> left=2 - mid=2, missing=arr[2] - (2+1) = 7 - 3 = 4 ≥ 3 -> right=1 Loop ends as left=2 > right=1. Return arr[right] + (k - (arr[right] - (right + 1))) = arr[1] + (3 - (4 - 2)) = 4 + (3 - 2) = 5.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Binary search narrows to index 1, final missing number is 5 [OK]
Hint: Trace missing count at mid carefully [OK]
Common Mistakes:
  • Off-by-one in missing calculation
  • Returning arr[left] instead of arr[right]
  • Miscomputing missing count as arr[mid] - mid
2. You are given a sorted array that has been rotated at an unknown pivot. You need to find if a target value exists in the array efficiently. Which algorithmic approach guarantees an optimal O(log n) time complexity for this problem?
easy
A. Find the pivot index using binary search, then perform binary search in the appropriate subarray.
B. Apply a greedy algorithm that picks the closest element to the target at each step.
C. Use a dynamic programming approach to store intermediate results for subproblems.
D. Linear search through the entire array until the target is found.

Solution

  1. Step 1: Understand problem constraints

    The array is sorted but rotated, so a simple binary search won't work directly.
  2. Step 2: Identify the optimal approach

    Finding the pivot splits the array into two sorted halves, allowing binary search on the correct half, achieving O(log n).
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Pivot finding plus binary search is classic for rotated sorted arrays [OK]
Hint: Pivot + binary search -> O(log n) [OK]
Common Mistakes:
  • Assuming linear search is efficient enough
  • Confusing greedy or DP with binary search
3. Consider the following Python code implementing the optimal solution for splitting an array into m subarrays minimizing the largest sum. Given nums = [7, 2, 5, 10] and m = 2, what is the final returned value?
easy
A. 10
B. 12
C. 17
D. 15

Solution

  1. Step 1: Calculate prefix sums

    prefix = [0, 7, 9, 14, 24]
  2. Step 2: Binary search on answer space [7,24]

    Check mid=15: can split into subarrays with max sum ≤15? Yes (e.g., [7,2,5] and [10]). Try lower mid=11: cannot split into 2 subarrays with max sum ≤11. Finally, minimal largest sum found is 12.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Minimal largest sum for splitting [7,2,5,10] into 2 parts is 12 [OK]
Hint: Trace binary search mid and feasibility check carefully [OK]
Common Mistakes:
  • Off-by-one in bisect leading to wrong subarray boundaries
  • Confusing max element with sum for low/high bounds
  • Miscounting number of splits in feasibility check
4. Suppose the problem is modified so that packages can be shipped in any order (not necessarily the given order) within the given days. Which of the following approaches correctly finds the minimal ship capacity under this new constraint?
hard
A. Use dynamic programming to assign packages to days minimizing maximum load per day, combined with binary search on capacity
B. Sort packages in descending order and use a greedy bin-packing heuristic combined with binary search on capacity
C. Use a simple greedy approach assigning packages to days in any order without binary search
D. Use the same binary search on capacity with the original feasibility check since order doesn't matter

Solution

  1. Step 1: Understand the relaxed constraint

    Packages can be shipped in any order, so the problem becomes a variant of multi-way partition or bin packing minimizing max load.
  2. Step 2: Identify correct approach

    Binary search on capacity still applies, but feasibility check requires DP or backtracking to assign packages optimally to days, as greedy heuristics may fail.
  3. Step 3: Why other options fail

    Original feasibility check assumes fixed order; greedy heuristics are approximate and may not find minimal capacity.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    DP feasibility check with binary search needed for unordered assignment [OK]
Hint: Order relaxation requires DP feasibility check, not simple greedy [OK]
Common Mistakes:
  • Using original feasibility check ignoring order change
  • Assuming greedy suffices
  • Ignoring complexity increase
5. Suppose the array can contain duplicate elements and the problem is modified so that elements can be reused multiple times when selecting the k closest elements to x. Which modification to the optimal binary search approach is necessary to handle this variant correctly?
hard
A. Use a priority queue to repeatedly pick the closest element allowing duplicates, instead of binary search.
B. Modify binary search to consider overlapping windows and allow repeated elements in the window.
C. No change needed; the existing binary search on window boundaries works as is.
D. Switch to a dynamic programming approach to track counts of reused elements.

Solution

  1. Step 1: Understand reuse of elements

    Allowing reuse means the window concept breaks because elements can appear multiple times, invalidating fixed window binary search.
  2. Step 2: Identify suitable approach

    A priority queue (min-heap) can repeatedly select the closest element to x, allowing duplicates and dynamic selection.
  3. Step 3: Why other options fail

    Binary search on windows assumes unique elements and fixed windows; DP is unnecessary and inefficient here.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Priority queue supports repeated selection, unlike fixed window binary search [OK]
Hint: Reuse breaks fixed window; priority queue handles duplicates [OK]
Common Mistakes:
  • Assuming binary search window works with reuse
  • Overcomplicating with DP when simpler heap suffices
  • Ignoring duplicates in window boundaries
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard