模版:左闭右开[lo, hi)
伪码如下:

def binarySearch(lo, hi) {
    while (lo < hi) {
        int mid = (hi-lo)/2+lo;
        if (f(m))    return m // 这一步是optional的
        if (g(m))    hi = mid; // 更新为[lo, mid)
        else    lo = mid+1;    // 更新为[mid-1, hi)
    }
    return lo or not found    // 返回的lo是满足g(m)的最小index
}

所以当我们要在一个有序的区间内寻找到第一个满足条件的数的时候,可以用binary search来查找。
如162题Find Peak Element这一题,需要我们找到一个局部峰值,题目要求的时间复杂度是logN,所以就想到可以用binary search来做,但是binary search怎么写一开始是没有思路的,比较nums[mid]和nums[mid-1],mums[mid+1]的大小吗?如果mums[mid] > mums[mid-1]该怎么搜索呢?

但是这一题我们会用O(N)的算法把数组扫描一遍得到峰值(这种方法我也没想出来,我一直在考虑和两边的值比较,但其实只要比较一边的大小就可以了)。也就是我们找到第一个符合nums[i] > nums[i+1]的i,就是我们要找的第一个峰值的index。代码如下:

class Solution {
    public int findPeakElement(int[] nums) {
        for(int i = 0; i < nums.length-1; i ++) {
            if (nums[i] > nums[i+1])
                return i;
        }
        return nums.length-1;
    }
}

看到这里就能立即联想到可以用binary search改写这段代码,代码如下:

class Solution {
    public int findPeakElement(int[] nums) {
        int lo = 0, hi = nums.length-1;
        while (lo < hi) {
            int mid = (hi-lo)/2+lo;
            if (nums[mid] > nums[mid+1])
                hi = mid;
            else    lo = mid+1;
        }
        return lo;
    }
}

再看一道非常基础的题目35、Find Insert Position,在一个sorted array中寻找某个数,如果这个数存在就返回它的index,如果不存在则返回它应该插入的位置。本质是一道求lower_bound的问题,也就是找到第一个使得nums[i] >= target的i,代码如下:

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

34题Find First and Last Position。在一个存在重复元素的sorted array里,给定一个target,找到target第一次出现的位置和最后一次出现的位置,如果不存在则返回-1。第一次出现的位置:求lowerbound,最后一次出现的位置:upperbound-1。代码如下:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[2];
        Arrays.fill(res, -1);
        if (nums == null || nums.length == 0)   return res;
        int lo = 0, hi = nums.length;
        while (lo < hi) {
            int mid = (hi-lo)/2+lo;
            if (target <= nums[mid])    hi = mid;
            else    lo = mid+1;
        }
        if (lo == nums.length || nums[lo] != target) return res;
        res[0] = lo;
        hi = nums.length;
        while (lo < hi) {
            int mid = (hi-lo)/2+lo;
            if (target < nums[mid]) hi = mid;
            else    lo = mid+1;
        }
        res[1] = lo-1;
        return res;
    }
}

我想做个正常人
1 声望2 粉丝