Skip to content

Binary Search

Published: at 05:07 PM

Binary Search

Table of Contents

Open Table of Contents

Use Case

Dealing with an Array-like structure, sorted or partially sorted.

Method

Core: cut the searching scope.

Steps:

Complexity:

Practice

Smallest larger than target

Given a target integer T and an integer array A sorted in ascending order, find the index of the smallest element in A that is larger than T or return -1 if there is no such index.

Assumptions

There can be duplicate elements in the array.

Solutions

public int smallestElementLargerThanTarget(int[] array, int target) {
    if (array == null || array.length == 0) {
        return -1;
    }
    int left = 0, right = array.length - 1;
    if (target >= array[right]) {
        return -1;
    }
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (array[mid] <= target) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left;
}

Closest

Given a target integer T and an integer array A sorted in ascending order, find the index i in A such that A[i] is closest to T.

Assumptions

There can be duplicate elements in the array, and we can return any of the indices with same value.

Solutions

public int closest(int[] array, int target) {
    if (array == null || array.length == 0) {
        return -1;
    }
    int left = 0, right = array.length - 1;
    if (target <= array[left]) {
        return left;
    }
    if (target >= array[right]) {
        return right;
    }
    while (left < right - 1) {
        int mid = left + (right - left) / 2;
        if (array[mid] == target) {
            return mid;
        } else if (array[mid] < target) {
            left = mid;
        } else {
            right = mid;
        }
    }
    if (Math.abs(array[left] - target) < Math.abs(array[right] - target)) {
        return left;
    } else {
        return right;
    }
}

K closest

Given a target integer T, a non-negative integer K and an integer array A sorted in ascending order, find the K closest numbers to T in A. If there is a tie, the smaller elements are always preferred.

Assumptions

A is not null
K is guranteed to be >= 0 and K is guranteed to be <= A.length

Return

A size K integer array containing the K closest numbers(not indices) in A, sorted in ascending order by the difference between the number and T.

Solutions

public int[] kClosest(int[] array, int target, int k) {
    int[] ans = new int[k];
    int n = array.length;
    int left = 0, right = n - 1;
    if (target <= array[left]) {
        for (int i = 0; i < k; i++) {
            ans[i] = array[i];
        }
        return ans;
    }
    if (target >= array[right]) {
        for (int i = 0; i < k; i++) {
            ans[i] = array[n - i - 1];
        }
        return ans;
    }
    int curr = 0;
    while (left < right - 1) {
        int mid = left + (right - left) / 2;
        if (array[mid] == target) {
            ans[curr++] = target;
            left = mid - 1;
            right = mid + 1;
            break;
        } else if (array[mid] < target) {
            left = mid;
        } else {
            right = mid;
        }
    }
    while (left >= 0 && right < n && curr < k) {
        if (Math.abs(array[left] - target) <= Math.abs(array[right] - target)) {
            ans[curr++] = array[left--];
        } else {
            ans[curr++] = array[right++];
        }
    }
    if (curr < k) {
        if (left > 0) {
            while (curr < k) {
                ans[curr++] = array[left--];
            }
        } else {
            while (curr < k) {
                ans[curr++] = array[right++];
            }
        }
    }
    return ans;
}

Notice: Mind the corner case when n == 2. The if condition should be (curr < k) rather than (curr < k - 1).

Search in unknown sized sorted array

Given a integer dictionary A of unknown size, where the numbers in the dictionary are sorted in ascending order, determine if a given target integer T is in the dictionary. Return the index of T in A, return -1 if T is not in A.

Assumptions

dictionary A is not null
dictionary.get(i) will return null(Java)/INT_MIN(C++)/None(Python) if index i is out of bounds

Solutions

public int search(Dictionary dict, int target) {
    if (target < dict.get(0)) {
        return -1;
    }
    int idx = 1;
    while (dict.get(idx) != null && dict.get(idx) < target) {
        idx *= 2;
    }
    int left = idx / 2, right = idx;
    if (dict.get(right) == null) {
        while (dict.get(right) == null) {
            right--;
        }
    }
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (dict.get(mid) == target) {
            return mid;
        } else if (dict.get(mid) < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

Search in bitonic array

Search for a target number in a bitonic array, return the index of the target number if found in the array, or return -1.

A bitonic array is a combination of two sequence: the first sequence is a monotonically increasing one and the second sequence is a monotonically decreasing one.

Assumptions:

The array is not null.

Solutions

// clarification: 
// Duplicate number in array? 
// What if find target both in ascending and descending?
public int search(int[] array, int target) {
    int n = array.length;
    if (n == 0) {
        return -1;
    }
    int peek = findPeek(array, n);
    if (array[peek] == target) {
        return peek;
    }
    int left = 0, right = n - 1;
    int leftRes = binarySearch(array, target, left, peek - 1, true);
    int rightRes = binarySearch(array, target, peek + 1, right, false);
    return Math.max(leftRes, rightRes);
}

private int findPeek(int[] array, int n) {
    if (n == 1) {
        return 0;
    }
    int left = 0, right = n - 1;
    while (left < right - 1) {
        int mid = left + (right - left) / 2;
        if (array[mid] < array[mid - 1]) {
            right = mid - 1;
        } else {
            left = mid;
        }
    }
    return array[left] < array[right] ? right : left;
}

private int binarySearch(int[] array, int target, int left, int right, boolean ascend) {
    if (left > right) {
        return -1;
    }
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (array[mid] == target) {
            return mid;
        } else if (array[mid] < target) {
            if (ascend) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        } else {
            if (ascend) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
    }
    return -1;
}

Previous Post
Recursion
Next Post
Coding Patterns | Two Pointers