一、寻找一个数(基本的二分搜索) 

int binary_search(int[] nums, int target) {
    int left = 0, right = nums.length - 1; 
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1; 
        } else if(nums[mid] == target) {
            // 直接返回
            return mid;
        }
    }
    // 直接返回
    return -1;
}

1、为什么 while 循环的条件中是 <=,而不是 <?

答:因为初始化right的赋值是nums.length - 1,即最后一个元素的索引,而不是nums.length。

这二者可能出现在不同功能的二分查找中,区别是:前者相当于两端都闭区间[left, right],后者相当于左闭右开区间[left, right),因为索引大小为nums.length是越界的。

2、为什么left = mid + 1,right = mid - 1?我看有的代码是right = mid或者left = mid,没有这些加加减减,到底怎么回事,怎么判断?

答:这也是二分查找的一个难点,不过只要你能理解前面的内容,就能够很容易判断。

刚才明确了「搜索区间」这个概念,而且本算法的搜索区间是两端都闭的,即[left, right]。那么当我们发现索引mid不是要找的target时,下一步应该去搜索哪里呢?

当然是去搜索[left, mid-1]或者[mid+1, right]对不对?因为mid已经搜索过,应该从搜索区间中去除。

3、为什么返回left而不是right?

答:都是一样的,因为 while 终止的条件是left == right。

二、寻找左侧边界的二分搜索

写法一

因为我们初始化 right = nums.length-1
所以决定了我们的「搜索区间」是 [left, right]
所以决定了 while (left <=right)

决定了下一步的二分区间为[left,mid-1] [mid+1,right]
同时也决定了 left = mid +1 和 right = mid-1

因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要立即返回
而要令 right = mid - 1锁定左侧边界

int left_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定左侧边界
            right = mid - 1;
        }
    }
    // 最后要检查 left 越界的情况
    if (left >= nums.length || nums[left] != target)
        return -1;
    return left;
}

写法二

因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)

决定了下一步的二分区间为[left,mid) [mid+1,right) 
同时也决定了right = mid 和  left = mid +1 

因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要立即返回
而要令 right = mid 锁定左侧边界

int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; // 注意

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