刷题--二分法(1)

首先是二分法的模板

 1 while(left + 1 < right){
 2   int mid = (left + right) >>> 1;
 3   if(array[mid] == target){
 4     //取决于实际操作           
 5   }else if(array[mid] > target){
 6     right = mid;
 7   }else(array[mid] < target){
 8   left = mid;
 9   }    
10 }

这样写的好处在于, 一定可以将搜索结果限定在left和right之间。即,如果array中有target的值,它一定是left或者right,如果没有,left和right就是按序在array中插入target后距离target最近的两个值。也就是说,left和right在退出while循环后仍然保持着先后顺序。

使用这个模板的时候注意的一点是,要在while循环之后对left和rihgt进行判断,判断的依据是极端情况。下面用三个例子说明。

1 array中没有重复的值,找到target值的索引。

极端情况下,target出现在头和尾,或者没有target,但是按照排序target可以插在头位置或者尾位置。

如果存在且在头位置,那么left不会变化,在尾位置right不会变化,而且在while中不会执行if(array[mid] == target)。

同样,如果不存在而按序target会出现在头位置,离target最近的是array[left],也就是头值;按序target在尾位置,最近的是array[right]。

2 array中有重复的值,找第一个出现的target

while(left + 1 < right){
   int mid = (left + right) >>> 1;
   if(array[mid] == target){
     right = mid;
    //缩小区间向左         
   }else if(array[mid] > target){
       right = mid;
   }else(array[mid] < target){
   left = mid;
   }    
}

首先, 当mid对应的值是target时,因为要找第一个出现的target,整体的区间要向左缩,也就是说如果是left = mid向右缩,就可能忽略第一个target,所以right = mid。

第二,极端情况下,第一个值就是target,在while循环中,right会不断向左缩,而left不会移动。最终array[left] == target。或者最后一个值是target,right不会移动,最终array[right] == target。

3 array中有重复的值,找最后出现的target

while(left + 1 < right){
   int mid = (left + right) >>> 1;
   if(array[mid] == target){ left = mid; //缩小区间向右 }else if(array[mid] > target){ right = mid; }else(array[mid] < target){   left = mid; } }

和第二种情况相似

首先, 当mid对应的值是target时,因为要找最后出现的target,整体的区间要向右缩,也就是说如果是right = mid向右缩,就可能忽略最后一个target,所以left = mid。

第二,极端情况下,第一个值就是target,在while循环中,right会不断向左缩,而left不会移动。最终array[left] == target。或者最后一个值是target,right不会移动,最终array[right] == target。

综上,在模板中不可避免的需要在完成while循环后进行一次判断,left和right都有可能是想要的答案。

猜你喜欢

转载自www.cnblogs.com/2333wzl/p/12242069.html