← Back to list

Binary Search Algorithm in Python

Hey, is this you?

Amit Yadav · 2024-07-20 17:06 · 1 claps · 9.6 min read
#binary-search #binary-search-algorithm #binary-search-python
Open on Medium ↗
Wiki topics: 💻 · Programming

Binary Search Algorithm in Python

Hey, is this you?

You want to learn Data Science but have no idea where to start?

I understand there’s an overwhelming amount of information out there, making it hard to even find a starting point.

Resources like courses and coaching programs that promise well-structured information often cost thousands of dollars.

But it doesn’t have to be this way.

I’ve curated 101+ free resources for you to learn Data Science in 90 days.

It covers:

  • Programming
  • Mathematics
  • Data Analytics
  • Machine Learning
  • And much more…

Why don’t you give it a try?

**Download Free Resources.**

Now let’s get back to the blog:

Binary search is a fundamental algorithm that every software engineer should have in their toolkit. It’s one of those concepts that, once you grasp it, you’ll find yourself using it repeatedly in various contexts. Let’s dive into what binary search is, why it’s so important, and when you should use it.

What is Binary Search?

Binary search is an efficient algorithm for finding an item from a sorted list of items. It works by repeatedly dividing in half the portion of the list that could contain the item until you’ve narrowed the possible locations to just one.

Think of it like searching for a word in a dictionary. You don’t start at the first word and flip through each page sequentially. Instead, you open the dictionary around the middle, check the word, and decide whether to go to the left or right half, effectively halving your search space each time. This is the essence of binary search.

Importance of Binary Search in Computer Science

You might wonder, why is binary search so important? The answer lies in its efficiency. Binary search operates in O(log n) time complexity, making it exponentially faster than a linear search (which has a time complexity of O(n)). This efficiency is crucial when dealing with large datasets.

In industry, the ability to search quickly through large amounts of data can mean the difference between a responsive application and a sluggish one. For example, if you’re working with a database of millions of user records, binary search can drastically reduce the time it takes to find a specific record compared to a linear search.

When to Use Binary Search?

Comparison with Linear Search

To understand when to use binary search, it helps to compare it with linear search. Linear search goes through each element in the list one by one until it finds the target or reaches the end. While simple, this approach can be very slow, especially for large lists.

Here’s a practical example: imagine you have a list of 1,000,000 numbers. If the number you’re looking for is at the end of the list, a linear search would potentially check every single number, making up to 1,000,000 comparisons. Binary search, on the other hand, would only need about 20 comparisons to find the number or determine it’s not in the list. That’s the power of reducing the problem size by half with each step.

Conditions When Binary Search is Applicable

  1. Sorted Data: The most critical condition for binary search is that the data must be sorted. Binary search relies on being able to eliminate half of the remaining elements at each step, which only works if the data is in a predictable order.
  2. Static Data: Binary search is most effective when the data set is static, meaning it doesn’t change often. If the list is frequently updated, you might need to resort it before performing a binary search, which can offset the efficiency gains.
  3. Random Access: The data structure should support random access, meaning you can quickly access any element by its index. This is why binary search is commonly used with arrays or lists but not with linked lists, where accessing an element by index takes linear time.

Examples from the Industry:

  • Database Indexing: Databases use binary search trees and B-trees, which leverage the principles of binary search to quickly locate records.
  • Coding Interviews: Binary search is a common topic in coding interviews. You might be asked to search for a number in a sorted array, find the square root of a number using binary search, or even use it as a subroutine in more complex algorithms.
  • Search Engines: When you type a query into a search engine, it uses variations of binary search to quickly return results from an indexed list of web pages.

Algorithm Steps

Understanding the step-by-step process of the binary search algorithm will help you grasp its efficiency and simplicity. I’ll walk you through the detailed steps and illustrate them with an example using a sorted array.

Detailed Step-by-Step Explanation

  1. Initialization
  • Start with two pointers, left and right, representing the bounds of the search area. Initially, left is set to 0 (the first index of the array), and right is set to the last index of the array.
  1. Middle Calculation
  • Calculate the middle index, mid, using the formula mid = left + (right - left) // 2. This helps avoid potential overflow issues in some programming languages when left and right are large.
  1. Comparison
  • Compare the target value with the middle element of the array:
  • If the target is equal to the middle element, you’ve found the target, and the search is complete.
  • If the target is less than the middle element, adjust the right pointer to mid - 1 to narrow the search to the left half of the array.
  • If the target is greater than the middle element, adjust the left pointer to mid + 1 to narrow the search to the right half of the array.
  1. Repeat
  • Repeat steps 2 and 3 until the left pointer exceeds the right pointer. If this happens, the target is not in the array, and the search concludes unsuccessfully.

Let’s put this into context with a concrete example.

Example with a Sorted Array

Imagine you have the following sorted array and you want to find the target value 19:

3, 6, 8, 12, 14, 19, 24, 30, 35, 42

Here’s how binary search works step-by-step:

  1. Initialization
  • left = 0
  • right = 9 (since there are 10 elements in the array)
  1. First Middle Calculation
  • mid = 0 + (9 - 0) // 2 = 4
  • The element at index 4 is 14.
  1. First Comparison
  • 19 (target) is greater than 14 (middle element).
  • Update left = mid + 1 = 5.
  1. Second Middle Calculation
  • mid = 5 + (9 - 5) // 2 = 7
  • The element at index 7 is 30.
  1. Second Comparison
  • 19 (target) is less than 30 (middle element).
  • Update right = mid - 1 = 6.
  1. Third Middle Calculation
  • mid = 5 + (6 - 5) // 2 = 5
  • The element at index 5 is 19.
  1. Third Comparison
  • 19 (target) is equal to 19 (middle element).
  • Target found at index 5.

Binary Search Algorithm in Python

Now that you understand the steps and importance of the binary search algorithm, let’s dive into how you can implement it in Python. I’ll walk you through both the iterative and recursive approaches, providing well-commented code snippets for each.

Python Implementation

Iterative Approach

The iterative approach involves using a loop to repeatedly narrow down the search range until the target is found or the range is exhausted.

Here’s the step-by-step process:

  1. Initialize Pointers: Start with left at the beginning of the array and right at the end.
  2. Loop Until Condition Met: Use a while loop to continue the search as long as left is less than or equal to right.
  3. Calculate Midpoint: Compute the midpoint of the current search range.
  4. Check Midpoint Value: Compare the midpoint value with the target:
  • If they are equal, return the midpoint index.
  • If the target is smaller, adjust the right pointer.
  • If the target is larger, adjust the left pointer.
  1. Target Not Found: If the loop exits, the target is not in the array.

Iterative Code Example:

def binary_search_iterative(arr, target):
    left, right = 0, len(arr) - 1  # Initialize pointers

    while left <= right:  # Loop until pointers meet
        mid = left + (right - left) // 2  # Calculate midpoint

        # Check if the midpoint is the target
        if arr[mid] == target:
            return mid  # Target found, return index
        elif arr[mid] < target:
            left = mid + 1  # Adjust left pointer
        else:
            right = mid - 1  # Adjust right pointer

    return -1  # Target not found

# Example usage
arr = [3, 6, 8, 12, 14, 19, 24, 30, 35, 42]
target = 19
result = binary_search_iterative(arr, target)
print("Target found at index:", result)

In this example, you can see how straightforward and efficient the iterative approach is. This method is typically easier to understand and implement for most use cases.

Recursive Approach

The recursive approach involves breaking the problem down into smaller sub-problems, which can be solved recursively. This method can be more elegant and is a good exercise in understanding recursion.

Here’s the step-by-step process:

  1. Base Case: If the search range is invalid (left exceeds right), return -1.
  2. Calculate Midpoint: Compute the midpoint of the current search range.
  3. Check Midpoint Value: Compare the midpoint value with the target:
  • If they are equal, return the midpoint index.
  • If the target is smaller, recursively search the left half.
  • If the target is larger, recursively search the right half.

Recursive Code Example:

def binary_search_recursive(arr, target, left, right):
    # Base case: if the search range is invalid
    if left > right:
        return -1

    mid = left + (right - left) // 2  # Calculate midpoint

    # Check if the midpoint is the target
    if arr[mid] == target:
        return mid  # Target found, return index
    elif arr[mid] < target:
        return binary_search_recursive(arr, target, mid + 1, right)  # Search right half
    else:
        return binary_search_recursive(arr, target, left, mid - 1)  # Search left half

# Example usage
arr = [3, 6, 8, 12, 14, 19, 24, 30, 35, 42]
target = 19
result = binary_search_recursive(arr, target, 0, len(arr) - 1)
print("Target found at index:", result)

In this recursive example, the function calls itself with updated pointers until it either finds the target or determines it’s not in the array. Recursion can make the code look cleaner and more intuitive, but it may also be less efficient in terms of memory due to the call stack.

Industry-Relevant Example

Let’s consider a real-world scenario in the e-commerce industry. Suppose you have a sorted list of product IDs, and you need to quickly check if a specific product ID is in your inventory. Using the binary search algorithm ensures that your search operations remain efficient, even as your product catalog grows.

Here’s how you might implement it:

product_ids = [1001, 1002, 1005, 1010, 1015, 1020, 1025, 1030, 1035, 1040]
search_id = 1025

# Iterative search
iterative_result = binary_search_iterative(product_ids, search_id)
print("Iterative: Product found at index:", iterative_result)

# Recursive search
recursive_result = binary_search_recursive(product_ids, search_id, 0, len(product_ids) - 1)
print("Recursive: Product found at index:", recursive_result)

Feel free to use these examples and explanations to enrich your understanding and application of binary search in your Python projects.

Analysis of Binary Search

Time Complexity

When analyzing algorithms, understanding their time complexity is crucial. Time complexity gives you an idea of how the runtime of an algorithm grows as the input size increases.

Best-Case Analysis

In the best-case scenario, the target value is found at the first middle element comparison. This happens when the target is located at the middle index of the initial array.

  • Time Complexity: O(1)

Worst-Case Analysis

In the worst-case scenario, the algorithm has to continually halve the search space until it is reduced to a single element. This occurs when the target is not in the array or is located at the very end.

  • Time Complexity: O(log⁡n)

Average-Case Analysis

On average, binary search will still have to reduce the search space logarithmically. The average case also has a time complexity of O(log⁡n).

Big O Notation Explanation

Big O notation helps you express the upper bound of an algorithm’s runtime. For binary search:

  • Best Case: O(1)
  • Worst Case: O(log⁡n)
  • Average Case: O(log⁡n)

This logarithmic time complexity is what makes binary search efficient, especially for large datasets.

Space Complexity

Iterative Approach

The iterative approach uses a constant amount of extra space, mainly for the variables used in the algorithm (e.g., left, right, and mid).

  • Space Complexity: O(1)

Recursive Approach

The recursive approach, however, involves function call overhead. Each recursive call adds a new frame to the call stack until the base condition is met. The depth of the recursion stack will be O(log⁡n)O(\log n)O(logn) in the worst case.

  • Space Complexity: O(log⁡n)

Comparison Between Iterative and Recursive Approaches

  • Iterative: More space-efficient with O(1) space complexity.
  • Recursive: Easier to read and write for those familiar with recursion but uses O(log⁡n) space due to the call stack.

Practical Applications

Binary search is widely used in various applications where quick lookup in a sorted dataset is required. Here are some practical use cases and real-world applications:

Use Cases

  • Searching in a Sorted List: Binary search is ideal for finding elements in a pre-sorted list of numbers or strings.
  • Database Indexing: Databases often use binary search trees (BSTs) and balanced trees like B-trees to maintain sorted data and facilitate efficient query operations.
  • Version Control Systems: Finding the exact version of a file where a bug was introduced can be sped up using binary search.
  • Networking: Binary search helps in routing and IP address lookup tables.

Real-World Applications

  1. Library Systems: Finding a book by its ISBN in a sorted catalog.
  2. E-Commerce Platforms: Quickly locating a product by its ID in a sorted product database.
  3. Operating Systems: Searching through file systems and memory pages.
  4. Competitive Programming: Many problems involving sorted arrays or search optimizations use binary search due to its efficiency.

Examples

Searching in a Sorted List of Numbers:

Imagine you have a sorted list of user IDs and need to verify if a particular user ID exists.

user_ids = [101, 205, 301, 404, 502, 601, 704, 801, 901, 1002]
target_id = 502
index = binary_search_iterative(user_ids, target_id)
print(f"User ID {target_id} found at index {index}")

Finding Elements in a Large Dataset:

Suppose you manage a large dataset of transaction records. Using binary search can help you quickly find a specific transaction ID.

transactions = sorted([random.randint(1000, 10000) for _ in range(1000000)])
target_transaction = 7500
index = binary_search_recursive(transactions, target_transaction, 0, len(transactions) - 1)
if index != -1:
    print(f"Transaction {target_transaction} found at index {index}")
else:
    print(f"Transaction {target_transaction} not found")

Conclusion

Binary search is a fundamental algorithm that offers significant efficiency improvements over linear search, especially with large datasets. By understanding both the iterative and recursive approaches, you can choose the best method for your specific use case. Its O(log⁡n)O(\log n)O(logn) time complexity makes it a valuable tool in any software engineer’s toolkit, and its applications range from simple list searches to complex database and networking operations.


메타데이터
post_id
4e4f8f0a9bb3
slug
binary-search-algorithm-in-python-4e4f8f0a9bb3
url
https://medium.com/@amit25173/binary-search-algorithm-in-python-4e4f8f0a9bb3
canonical_url
https://medium.com/@amit25173/binary-search-algorithm-in-python-4e4f8f0a9bb3
author_url
https://medium.com/@amit25173
status
ok
fetched_at
2026-06-24 04:09:36