一、原理及适用条件
1、当数组有“二段性”时就能使用(注意不仅仅是递增或递减的有序数组)
2、用找到的 mid 值来比较二段之间的不同,进而得出边界的移动情况。
二、一般二分法
1、适用场景
此时要找的目标值只有一个,存在或不存在,就对应找到或没找到。
2、模板
while(left <= right)
{
int mid = (right - left) / 2 + left;
if(....)
left = mid + 1;
else if(...)
right = mid - 1;
//找到了
else
return ...
}
//没找到
return ....;
3、例题
三、左右边界二分
1、总结模板
此题不同于一般二分,他的target是可能出现多次,找到一个不是找到答案,不难看出是要找数组中target的左右边界。
(1)左边界
此时我们要得到左边界就要让left与right相遇,我们知道在这张图上left在左区间永远不合法,所以left每次移动必定是越过mid,争取能到右区间与right相遇,右区间是永远合法,所以right每次移动万万不能越过mid,不然无法维持右区间特性。
左区间模板
while(left < right)
{
int mid = (right - left) / 2 + left;
if(...)
left = mid + 1;
else
right = mid;
}
//最后left与right相遇,但是不一定就是最终答案,要判断特殊情况
....
(2)右边界
与左区间类似。
右区间模板
while(left < right)
{
int mid = (right - left + 1) / 2 + left;
if(...)
left = mid;
else
right = mid - 1;
}
//最后left与right相遇,但是不一定就是最终答案,要判断特殊情况
....
2、细节问题
(1)为什么判断条件是 left < right
在一般二分中,判断条件是 left <= right,是因为答案就一个,如果答案存在就要在循环里面直接返回,因为left一次移动到mid左边,right移到mid右边,左右指针不可控,不像左右边界模板,至少有一个指针永远不会超出合法范围。所以要用一般二分,就一定要在循环里面返回正确结果,或在循环外面返回找不到。
从上面表述来看其实所有的一般二分都能转化成左右边界二分。
那为什么边界二分left和right不能相等?
在有结果时,即left == right,进入循环,mid == left,mid == right,此时必有一个判断条件让left = mid 或 right = mid,导致死循环。
(2)mid如何计算
左边界 mid = (right - left) / 2 + left; 要选中点靠左的,因为当左右区间只有两个元素时,选中点靠右的当要执行 right = mid时,right不会左移导致死循环。
右边界 mid = (right - left + 1) / 2 + left;
四、例题
五、经验
1、一上来就要找二段性!!!!!!!!
2、结果一定要在等于的区间。
3、left与right相遇不一定是结果,要考虑特殊情况!!!!!!
4、思考时不要想模板,要找到具体每道题的分类讨论正确移动指针方式,不要被模板限制思维。