The Ultimate Guide to Using Binary Search on Rotated and Repeated Arrays !

The Ultimate Guide to Using Binary Search on Rotated and Repeated Arrays !

Binary search is a fundamental algorithmic technique used to efficiently locate elements in a sorted array. It is always used when the array is sorted. But what do you do if there are multiple elements in that array or the array is sorted but rotated on some random pivot ?

1. Searching in a Rotated Sorted Array

The Problem

Imagine you are given a sorted array that has been rotated at an unknown pivot, and you need to find the index of a specific target value. If the target does not exist in the array, the function should return -1. This problem demands an O(log n) runtime complexity, suggesting a binary search-based approach.

Understanding the Rotated Array

A rotated sorted array is one where the sorted order has been shifted to the left or right by some pivot. For example, [4, 5, 6, 7, 0, 1, 2] is a sorted array rotated at pivot 3.

To solve this, we need to identify which part of the array (left or right of the mid-point) is sorted and decide where to continue the search.

Approach

We can modify the binary search algorithm with the following steps:

  1. Initialize Pointers: Set two pointers, lo and hi, at the start and end of the array, respectively.

  2. Binary Search Loop: While lo is less than or equal to hi:

    • Calculate the middle index, mid.

    • Check if the middle element nums[mid] is the target. If yes, return its index.

    • Determine which part of the array is sorted:

      • If the left part (nums[lo] to nums[mid]) is sorted:

        • Check if the target lies within this range. If yes, adjust hi to mid - 1.

        • Otherwise, adjust lo to mid + 1 to search in the right part.

      • If the right part (nums[mid] to nums[hi]) is sorted:

        • Check if the target lies within this range. If yes, adjust lo to mid + 1.

        • Otherwise, adjust hi to mid - 1.

  3. Return the Result: If the target is not found, return -1.

Code Implementation

class Solution {
    public int search(int[] nums, int target) {
        int lo = 0;
        int hi = nums.length - 1;

        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;

            if (nums[mid] == target) {
                return mid;
            }

            // Determine which part is sorted
            if (nums[lo] <= nums[mid]) { // Left part is sorted
                if (nums[lo] <= target && target < nums[mid]) {
                    hi = mid - 1; // Target is in the left part
                } else {
                    lo = mid + 1; // Target is in the right part
                }
            } else { // Right part is sorted
                if (nums[mid] < target && target <= nums[hi]) {
                    lo = mid + 1; // Target is in the right part
                } else {
                    hi = mid - 1; // Target is in the left part
                }
            }
        }
        return -1; // Target not found
    }
}

Complexity

  • Time Complexity: O(log n) due to the binary search reducing the search space by half each iteration.

  • Space Complexity: O(1) since only a few extra variables are used.

2. Finding the Range of a Target in a Sorted Array

The Problem

Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. The challenge here is to efficiently find both occurrences using a modified binary search.

Why Simple Binary Search Isn't Enough

A regular binary search will find an occurrence of the target, but not necessarily its first or last position. To find both, we need to perform two separate binary searches: one to find the first occurrence and another to find the last.

Approach

  1. First Binary Search: To find the last occurrence of the target:

    • Set si (start index) to 0 and ei (end index) to nums.length - 1.

    • Use a binary search; if nums[mid] equals the target, set the result's end index (res[1]) to mid and continue searching in the right half.

  2. Second Binary Search: To find the first occurrence of the target:

    • Reset si to 0 and ei to nums.length - 1.

    • Use a similar binary search; if nums[mid] equals the target, set the result's start index (res[0]) to mid and continue searching in the left half.

  3. Return the Result: The array res contains the starting and ending indices of the target value.

Code Implementation

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int si = 0, ei = nums.length - 1;
        int[] res = {-1, -1};  // Initialize result array

        // First binary search to find the last occurrence
        while (si <= ei) {
            int mid = si + (ei - si) / 2;
            if (target < nums[mid]) {
                ei = mid - 1;
            } else if (target > nums[mid]) {
                si = mid + 1;
            } else {
                res[1] = mid;  // Update end index
                si = mid + 1;  // Continue searching in the right half
            }
        }

        // Reset the pointers for the second binary search
        si = 0;
        ei = nums.length - 1;

        // Second binary search to find the first occurrence
        while (si <= ei) {
            int mid = si + (ei - si) / 2;
            if (target < nums[mid]) {
                ei = mid - 1;
            } else if (target > nums[mid]) {
                si = mid + 1;
            } else {
                res[0] = mid;  // Update start index
                ei = mid - 1;  // Continue searching in the left half
            }
        }

        return res;  // Return the result array
    }
}

Complexity

  • Time Complexity: O(log n) for each binary search, making the total complexity O(log n).

  • Space Complexity: O(1) since only a constant amount of extra space is used.

Conclusion

In this article, we explored two advanced applications of binary search to handle more complex scenarios: searching in a rotated sorted array and finding the range of a target value in a sorted array. By modifying the standard binary search approach, we efficiently solved these problems with an O(log n) time complexity. These techniques showcase the versatility of binary search and how small adjustments can make it suitable for a variety of problem-solving contexts.