首先是二分法的模板
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都有可能是想要的答案。
来源:https://www.cnblogs.com/2333wzl/p/12242069.html