前几次写二分查找时经常会遇到无限循环的问题,例如下面这道题。
这个问题分为两部分:寻找>=目标的第一个数,以及<=目标的最后一个数(这里数的顺序是从左往右)。
寻找>=目标的第一个数很简单,代码如下
while (l < r)
{
int mid = (l + r) >> 1;
if (a[mid] >= tgt) r = mid; /*tgt即为要查找的元素*/
else l = mid + 1;
}
而寻找<=目标的最后一个数却经常写错,写成下面的这种形式:
while(l < r)
{
int mid = (l + r) >> 1;
if (a[mid] <= tgt) l = mid;
else r = mid - 1;
}
为什么上面不会无限循环而下面会呢?
关键在于mid与l的变化。
我们先看错误的代码。
由于mid = (l + r) >> 1,即为(l + r) / 2,这个算式是向下取整的,假设l = 4,r = 5,那么mid的取值会是4,与l相等。假设在上一轮循环中,l = 3,r = 5,mid = 4,并且此时满足if语句,通过l = mid将l的值转变为4,那么在下一轮循环中mid的值依旧为4,且一定会继续满足if语句执行l = mid,l变量没有丝毫变化。 也就是会无限循环l与r相邻的情况,由于向下取整的特性,l会不断赋值为与其相等的mid。
我们再看上面不会出错的代码。
在这个代码里,l的赋值代码为l = mid + 1,并不会出现上面的错误。而因为mid是向下取整的,当l与r相邻时mid = l,所以r = mid并不会使其死循环。
上面解释了由于mid向下取整的原因会导致l = mid不断循环,所以这里提供一个最简单的解决方法,那就是将mid向上取整,令mid = (l + r + 1) >> 1 即可实现,代码如下:
while(l < r)
{
int mid = (l + r + 1) >> 1;
if (a[mid] <= tgt) l = mid;
else r = mid - 1;
}
因篇幅问题不能全部显示,请点此查看更多更全内容