严总讲解整数情况二分查找(二分查找算法模板)的时候,给了两个模板。为什么是两个,一个不行么?对于整数的情况,不可以。原因其实是整数需要取整,它即可以上取整也可以下取整
。
从算法角度来考量,二分查找只有一种算法,这个和闫总所讲的浮点数的二分法一致。同样的,闫总给的整数情况的两个模板,从算法角度来说没有区别
。它们真正的区别是由于不同的取整情况造成的。
假设要处理的区间为[3,8],即l == 3, r == 8
, 此时,mid = ?如果说按照浮点数来计算的话,mid = 5.5,但是mid是整数,它的值要么下取整取5,要么上取整取6。
回到二分算法本身,第一步其实就是把区间[l,r]不重不漏且一分为而的分为[l, a]和[b, r],其中, b== a+ 1
。但是,a和b该如何取值。如果,r - l = 奇数,也就是r = l + 奇数,(r + l)/2.0一定是**.5
,这个时候区间包含有偶数个元素一定可以一分为二。比如[3,8]可以刚好平均分为[3, 5]和[6, 8]两个区间,大家可以发现此时,mid本该为5.5,而5和6这两个数其实对应的就是mid下取整和上取整后的结果。而对于,r-l = 偶数的情况,比如[3, 9],mid = 6,这时如果想要把区间平均一分为2就不可能了,但观察下如果计算[3, 9]的中心取下整为(9+3)/2 = 6,取上整(9 + 3 + 1)/2 = 6,为了理解我们可以“偷“用6一下,把[3, 9]划分为[3, 6]和[6, 9](在实际程序中不可能,因为要么mid + 1 要么 mid - 1)。 结合二分查找算法,只要是check(闫总算法)运行了以后,没有被“选中”的另一半区间一定被丢弃。所以在实际程序中,[3, 6]、[6, 9]这两个区间都有可能被选到(与问题本身相关),倘若选中了其中之一则另一个一定不包含mid。
所以,对于整数的二分,就是将[l, r]划分为[l, mid下取整]和[mid上取整, r]两个区间,并在其中选则其一继续二分。注意,整个区间划分过程都要确保无遗漏,不重复,而且划分过程要保证一致,即如果是l = [mid,r]即选择了[mid上取整, r]这个区间,则所有的mid都要取上整,即赋值的时候 mid = (l + r + 1) >> 1。所以,这也就是闫总所讲的,第一步先不管,先写上mid = (l + r)/2,第二再看是l = mid 还是 r = mid,如果是l = mid那就是上取整即要再加1,如果是r = mid 则下取整。
总结,需不需要“+1”和check没关系,是根据check结束后所得的结果来判定到底是取上整还是取下整,如果取上整则该段程序都取上整,取下整则都取下整。其最终本质都是为了不重复的划分区间以二分找到最后的边界。
题外话
- 二分法在求mid = (l + r)/2,这么写其实是一个有bug的程序,因为当l和r都特别大(接近MAX int)的时候mid会得到一个负数。所以,mid应该写成
mid = l + (r-l)/2
,上取整为mid = l + (r - l + 1)/2
。 - 在计算机科学中,区间一般约定俗成的取左闭右开[0, n),比如数组维度就是[0,n),C++ iterator[begin(), end()),python中也如此。如果二分法把区间也定义为[l, r),就不存在上下取整的问题,区间划分比较统一。这个时候就只有一个模版。在Youtube上找到了我认为讲解二分最为清楚的一个视频,Binary Search - A Different Perspective | Python Algorithms
【3,9】区间向上取整应该是7吧
翻墙看吗
悟了!
看了YouTube的视频 确实不错!!