首先是二分查找,举个有序的整数数组例子(二分查找和搜索都是针对有序数组)

public int rank(int key, int n) {
        int lo = 0, hi = n - 1;
        while (lo <= hi) {
            int mid = lo + ((hi - lo) >> 1); //>>1是除以2
            // 为什么不直接(lo+hi)>>1呢,因为lo+hi可能溢出,而hi-lo不溢出,lo+(hi-lo)>>1是小于hi的,也不溢出,更安全
            int cmp = key - a[mid];// a为有序数组
            if (cmp < 0) {
                hi = mid - 1;
            } else if (cmp > 0) {
                lo = mid + 1;
            } else {
                return mid;
            }
        }
        return lo;
    }



如果查找到,返回数组下标mid,如果没找到,return lo;有人会问了为什么返回lo??当然你非要在找不到的情况下返回一个负数比如-1也可以。这就是关键所在,假设a[10]={1, 2, 5, 7, 7, 12, 13, 17, 18, 20};我要查找的key是6。

第一步:lo   hi   mid

             0     9     4        

             0     3     1        a[4]=7 > key=6,所以hi=mid - 1=3;    lo=0  <=  hi=3继续循环,mid=(lo+hi)/2=1

             2     3     2        cmp = 6-a[1] > 0,所以lo=mid+1=2,lo=2  <=  hi=3继续循环,mid=(lo+hi)/2=2

             3     3     3        cmp = 6-a[2] > 0,所以lo=mid+1=3,lo=3  <=  hi=3继续循环,mid=(lo+hi)/2=3

 3     2               cmp = 6-a[3] < 0,所以hi=mid-1=2,此刻不满足套件,跳出,返回lo=3

    返回3什么意思?你看看这个数组,是不是小于6有3个元素?

        lo的值正好等于数组中小于被查找的元素的数量

         那怎么知道有没有找到呢?返回的到底是命中的下标还是小于被查找元素数量呢? 

            用  i=rank(key, n); // 在n个元素的数组查找key=6,返回下标传给i

(i<n&&a[i] == key){表示找到了}

                      else  {没找到}

               一些有序集合判断,如果集合包含这些元素,就更新数值,if(i<n&&a[i] == key){更新数值}。或者要插入的位置在哪里?假如lo=5,我查找一遍,就知道他前面有5个元素,即我这次要插入的元素下标就为5(从0开始计算)


下面讲一下二分搜索

比如从有序数组中查找某个数值

                                                                                lower_bound

    给定长度为n的单调不下降数列a0, a1,...an-1和一个数k,求满足ai≥k条件的最小的i。不存在的情况输出n。

 限制条件

106

≤a0≤a1≤...≤an-1<109

≤k≤109

   输入

   n = 5

   a = {2, 3, 3, 5, 6}

   k = 3

  输出

    1(其中a0<3, a1>=3)

  这里不仅仅是二分查找了,不仅是找到下标,而是找到最小的下标

  直接上代码(关键部分)

int n, k;
    int[] a = new int[1000001];
    public void solve() {
        int lo = 0, hi = n;
        while (hi > lo) {
            int mid = lo + ((hi - lo) >> 1);
            if (a[mid] >= k) {
                hi = mid;
            } else {
                lo = mid;
            }
        }
        System.out.println(hi);// 最后hi和lo一样,就算找不到的情况返回n,这种情况下lo=hi=n,返回哪个都一样
    }

比如a[5]={2, 3, 3, 5, 6}

a[2]=3和3进行比较,可以知道解不大于2

a[1]=3和3比较,可以知道解不大于1

a[0]=2和3比较,可以知道解不小于0

所以解为1

二分搜索法是通过不断缩小解的可能存在的范围,从而求得问题最优解的方法。


========================================Talk is cheap, show me the code=======================================