Array Binary Search

典型的 binary search 题,我还是比较喜欢用 left + 1 < right,不用担心各种奇葩输入导致的越界。

public class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int leftEnd = left, rightEnd = right;
        while(left + 1 < right){
            int mid = left + (right - left) / 2;
            if(nums[mid] >= target){
                right = mid;
            } else {
                left = mid;
            }
        }
        if(nums[left] == target) leftEnd = left;
        else if(nums[right] == target) leftEnd = right;
        else leftEnd = -1;

        left = 0;
        right = nums.length - 1;

        while(left + 1 < right){
            int mid = left + (right - left) / 2;
            if(nums[mid] <= target){
                left = mid;
            } else {
                right = mid;
            }
        }
        if(nums[right] == target) rightEnd = right;
        else if(nums[left] == target) rightEnd = left;
        else rightEnd = -1;

        int[] rst = new int[2];
        rst[0] = leftEnd;
        rst[1] = rightEnd;

        return rst;
    }
}

这道不是 LinkedIn 面经题。

是我被 DP 虐多了之后,来无耻地灌个水找找自信。。

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int left = 1;
        int right = n;
        while(left + 1 < right){
            int mid = left + (right - left) / 2;
            if(isBadVersion(mid)){
                right = mid;
            } else {
                left = mid;
            }
        }

        if(isBadVersion(left)) return left;
        else if(isBadVersion(right)) return right;

        return -1;
    }
}

这道不是 LinkedIn 面经题。

是我被 DP 虐多了之后,来无耻地灌个水找找自信。。

public class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while(left + 1 < right){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return mid;
            } else if(nums[mid] < target){
                left = mid;
            } else {
                right = mid;
            }
        }

        if(target <= nums[left]) return left;
        if(target <= nums[right]) return right;
        if(target > nums[right]) return right + 1;

        return 0;
    }
}

这题在地里看到过,当时把猴子给弄挂了。。

这题很重要的性质是,没有重复元素。

可以根据 A[mid] 与 A[right] 的大小关系,先行判断 mid 一定位于哪一端;

对于已经确定 mid 左/右 的数组,必然有一段区间满足单调性,可以利用来做 binary search.

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

        while(left + 1 < right){
            int mid = left + (right - left) / 2;

            if(nums[mid] >= nums[right]){
                if(target <= nums[mid] && target >= nums[left]){
                    right = mid;
                } else {
                    left = mid;
                }
            } else {
                if(target >= nums[mid] && target <= nums[right]){
                    left = mid;
                } else {
                    right = mid;
                }
            }
        }

        if(nums[left] == target) return left;
        if(nums[right] == target) return right;

        return -1;
    }
}

在把上一道题做完之后,这题就是一个乞丐版的 search in rotated sorted array. 因为已知没有 duplicates 就更简单了。

public class Solution {
    public int findMin(int[] nums) {
        int left = 0;
        int right = nums.length - 1;

        while(left + 1 < right){
            int mid = left + (right - left) / 2;
            if(nums[mid] >= nums[right]){
                left = mid;
            } else {
                right = mid;
            }
        }

        return Math.min(nums[left], nums[right]);
    }
}

加上有重复元素的条件之后,这题立刻就变成 Hard 难度了。原因在于出现了新情况,即我们不知道最小值到底在 mid 的左边还是右边,比如【3,[3],1,3】,中间的 3 和两端值都相等,无法正确地直接砍掉一半。

  • 我们依然可以靠 A[mid] 与 A[right] 的大小关系来二分搜索;

    • A[mid] > A[right] 时,mid 在左半边,最小值一定在 mid 右侧;

    • A[mid] < A[right] 时,mid 在右半边,最小值一定在 mid 左侧;

  • A[mid] == A[right] 时,无法判断,把 right 向左挪一格。

public class Solution {
    public int findMin(int[] nums) {
        int left = 0;
        int right = nums.length - 1;

        while(left + 1 < right){
            int mid = left + (right - left) / 2;
            if(nums[mid] > nums[right]){
                left = mid;
            } else if(nums[mid] < nums[right]){
                right = mid;
            } else {
                right --;
            }
        }

        return Math.min(nums[left], nums[right]);
    }
}

Last updated