二分法用于在有序数列中查值或查范围。时间复杂度O(logn)
    对于查值,正统的模板如下。low与high的更新都是mid+1或mid-1,循环条件为low<=high。这种写法二分的效率最高。

    1. class Solution {
    2. public int search(int[] nums, int target) {
    3. int low = 0, high = nums.length - 1;
    4. while (low <= high) {
    5. int mid = (high - low) / 2 + low;
    6. int num = nums[mid];
    7. if (num == target) {
    8. return mid;
    9. } else if (num > target) {
    10. high = mid - 1;
    11. } else {
    12. low = mid + 1;
    13. }
    14. }
    15. return -1;
    16. }
    17. }

    对于一般的查找,可以使用以下模板:
    思路一:
    以获得第一个>=target的数的下标为例,可画出如下图。红字“二分位置”是满足条件的位置。使用一个额外的变量 pos 记录满足条件的位置。在判断条件时,结合下图决定给 pos 赋值应该放在哪个条件块中。

    二分法 - 图1

    1. // 以获得第一个>=target的数的下标为例
    2. public int my_binary_search(int nums[], int target){
    3. int low = 0, high = nums.length - 1, pos=-1;
    4. while (low <= high) { // 此种循环最终必然会导致 left > high
    5. int mid = (high - low) / 2 + low;
    6. if(nums[mid] < target){
    7. low=mid+1;
    8. }else{
    9. pos=mid; // else条件块代表紫色区域,是有可能作为结果的,在此处更新pos的值
    10. high=mid-1;
    11. }
    12. }
    13. return pos;
    14. }

    思路二:
    以获得第一个>=target的数的下标为例,可画出如下图。红字“二分位置”是满足条件的位置。二分就是通过缩小[low, high]的范围锁定这个位置,因此每次更新的 low与high都应该是【可能满足条件的位置】。
    如果nums[mid] < target,说明mid位置不可能满足条件了,因此 low=mid+1;
    如果nums[mid] >= target,说明mid位置【有可能满足条件】,因此 high=mid;
    最后一定会将[low, high]缩小至low==high,返回high。由于high有可能一直没有更新(比如全部数都二分法 - 图2

    1. // 以获得第一个>=target的数的下标为例
    2. public int my_binary_search(int nums[], int target){
    3. int low = 0, high = nums.length - 1;
    4. while (low != high) { //此种循环最终必然会将问题规模缩减至1
    5. int mid = (high - low) / 2 + low;
    6. int noUse = nums[mid] < target ? (low=mid+1) : (high=mid);
    7. }
    8. if(high==nums.length-1 && nums[high]<target){
    9. return -1; // 没有>=target的数
    10. }
    11. return high;
    12. }