二分搜索 这里有一篇总结二分搜索非常好的Blog,链接贴在这,一定要看!!
传送门 
一. 二分搜索基本应用场景 当一道题目出现了“有序N维数组中查找某个东西或最接近的值”时,大概率用二分搜索;或者输入的数字特别大,达到2^31 - 1左右的这种,也很有可能使用二分搜索。
其核心思想就是每次找一半,然后舍弃另一半,其中最恶心的莫过于有重复数字的二分搜索,太难顶了。例如:LeetCode 81 的“Search in Rotated Sorted Array II”,到现在也不懂,真的难受,先记录着,等以后能搞懂了再回来看。
二. 二分搜索模板 基本模板 关于查找区间的问题,究竟是“左闭右开”还是“左右闭区间”,我在这里直接就用左右全闭区间的方法了。
1 2 3 4 5 6 7 8 9 10 11 while  (l <= r) {    m = l + (r - l) / 2 ;     if  g (m)           return ;     else  if  g (m)  > target :         r  = m - 1 ;    else  if  g (m)  < target :         l  = m + 1 ;} 
最终返回的时候,l一定是r右面的那个数,这是由while的循环条件决定的,二者最后只能差1。最后看情况是需要l还是r。
左边界 & 右边界 参考题目:下面例题 3.4 LeetCode 34. Find First and Last Position of Element in Sorted Array 
关于左边界和右边界的问题只要简单说一下就懂了,举个例子,我们有一个数组nums:[1, 2, 6, 6, 10, 13, 17],我们想要找出数字6所在的位置,那么我们返回2还是返回3呢?虽然值是不同,但是对于实际应用的时候有很大不同。
比如说对于查找的时候,可能需要查找大于某个数的最小值,或者小于某个数的最大值,前者就是要找右边界,后者就是要找左边界(这里不敢保证写的绝对对啊,只是个人理解,有可能是错的)。
对于左右边界的处理其实不同点就在于等于的情况下,是更新left还是right。
左边界 -> 等于的时候更新右边界
1 2 3 4 5 6 7 8 while  (l <= r) {    m = l + (r - l) / 2 ;     if  g (m)  >         r = m - 1 ;     else  if  g (m)  < target :         l  = m + 1 ;} 
右边界 -> 等于的时候更新左边界
1 2 3 4 5 6 7 8 while  (l <= r) {    m = l + (r - l) / 2 ;     if  g (m)  > target :         r  = m - 1 ;    else  if  g (m)  <         l = m + 1 ; } 
返回l?返回r? 我们需要care的一点就是到底是返回l还是返回r呢?结论是:就题论题,九成以上返回l,但是不排除返回r的可能,例如上面的数组[1, 2, 6, 6, 10, 13, 17],找出比6小的最大值,自己写一下代码,很容易发现返回的是r,而不是l,因此如果不确定的话,最好的办法是找个例子尝试一下。
三. 二分搜索简单题目 其实二分搜索,难的是真滴难,简单的也有很多坑,慢慢积累经验吧,有时候边界条件搞不懂就特别烦。
注意: 需要注意的点就是输入的值可能会特别大,二分后的值,如果直接平方的话有可能会越界,所以可以考虑使用除法或者用long来实现。这里用除法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution      public  int  mySqrt (int  x)           if  (x <= 1 ) {             return  x;         }         int  l = 0 , m = 0 , r = x;         while  (l <= r) {             m = l + (r - l) / 2 ;             if  (m == x / m) {                 return  m;             } else  if  (m > x / m) {                 r = m - 1 ;             } else  if  (m < x / m) {                 l = m + 1 ;             }         }         return  l - 1 ;     } } 
注意: 需要注意的是这里不能用除法,和69题不一样,因为除法会损失精度,导致计算错误,所以这里用long类型来辅助计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Solution      public  boolean  isPerfectSquare (int  num)           int  start = 1 , end = num;         long  target = 0 ;         while  (start <= end) {             target = (start + end) / 2 ;             if  (target * target > num) {                 end = (int )target - 1 ;             } else  if  (target * target < num) {                 start = (int )target + 1 ;             } else  {                 return  true ;             }         }         return  false ;     } } 
注意: 这道是最传统的二分搜索,基础中的基础。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution      public  int  searchInsert (int [] nums, int  target)           int  l = 0 , m = 0 , r = nums.length - 1 ;         while  (l <= r) {             m = l + (r - l) / 2 ;             if  (nums[m] == target) {                 return  m;             } else  if  (nums[m] < target) {                 l = m + 1 ;             } else  if  (nums[m] > target) {                 r = m - 1 ;             }         }         return  l;     } } 
注意: 先找到一个pivot,再分别计算左右有多少。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class  Solution      public  int [] searchRange(int [] nums, int  target) {         int  l = 0 , r = nums.length - 1 , m = 0 , pivot = -1 ;         int [] res = new  int [2 ];         while  (l <= r) {             m = l + (r - l) / 2 ;             if  (nums[m] < target) {                 l = m + 1 ;             } else  if  (nums[m] >= target) {                 r = m - 1 ;             }         }         if  (l == nums.length || nums[l] != target) {             return  new  int []{-1 , -1 };         }                  res[0 ] = l;         r = nums.length - 1 ;         while  (l <= r) {             m = l + (r - l) / 2 ;             if  (nums[m] > target) {                 r = m - 1 ;             } else  {                 l = m + 1 ;             }         }         res[1 ] = l - 1 ;         return  res;     } } 
注意: 这道题要用二分法来减少运算,次数,而且负数除以2余数为-1,注意取反。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class  Solution      public  double  myPow (double  x, int  n)           if  (x == 0d ) {             return  0 ;         }         if  (n == 0 ) {             return  1 ;         } else  if  (n > 0 ) {             double  ret = myPow(x, n / 2 );             if  (n % 2  == 1 ) {                 return  x * ret * ret;             } else  {                 return  ret * ret;             }         } else  {             double  ret = myPow(x, n / 2 );             if  ((0  - n) % 2  == 1 ) {                 return  ret * ret / x;             } else  {                 return  ret * ret;             }         }     } }