Binary Search in action

Jul 26, 2023

We can use Binary Search to search for an element with just O(log n) time complexity —> belongs to Divide and Conquer algorithmic technique.

Let’s take an example with problem 34 on Leetcode:

Find First and Last Position of Element in Sorted Array

Input: nums = [5,7,7,8,8,10], target = 8

Output: [3,4].

The first approach - O(n)

Which is simply loop over an array and find the index, but the worst-case will lead to O(n) time complexity.

var searchRange = function (nums, target) {
  if (!nums.length) {
    return [-1, -1];
  }
  const result = [];
  for (let i = 0; i <= nums.length - 1; i++) {
    if (nums[i] === target) {
      if (result.length === 0) {
        result.push(i);
      }
      if (nums[i + 1] !== target) {
        result.push(i);
        return result;
      }
    }
  }

  return [-1, -1];
};

The second approach - O(log n)

Now we’ll use Binary Search to solve this problem.

The pseudo-code template for Binary Search:

FUNCTION binarySearch(array, target):
    left = 0
    right = length(array) - 1

    WHILE left <= right:
        mid = left + (right - left) / 2

        IF array[mid] == target:
            RETURN mid
        ELSE IF array[mid] < target:
            left = mid + 1
        ELSE:
            right = mid - 1

    RETURN -1
END FUNCTION

Remember the +1 to left and -1 to right to avoid infinite loop (especially when the loop condition has the = sign).

Then, let’s apply this to the real code:

var searchRange = function (nums, target) {
  if (!nums.length) {
    return [-1, -1];
  }

  let start = -1;
  let end = -1;

  // find left
  let l = 0;
  let r = nums.length - 1;

  while (l <= r) {
    const mid = Math.floor((l + r) / 2);
    if (nums[mid] === target) {
      r = mid - 1;
      start = mid;
    } else if (nums[mid] < target) {
      l = mid + 1;
    } else {
      r = mid - 1;
    }
  }

  // find right
  l = 0;
  r = nums.length - 1;

  while (l <= r) {
    const mid = Math.floor((l + r) / 2);
    if (nums[mid] === target) {
      l = mid + 1;
      end = mid;
    } else if (nums[mid] < target) {
      l = mid + 1;
    } else {
      r = mid - 1;
    }
  }

  return [start, end];
};

This approach will take O(log n) time complexity even in the worst-case.