关于二分法的一些问题
以搜索区间为[0, len -1]为例子
左中位数下标数: (0 + len - 1) >>> 1
右中位数下标数: (0 + len - 1 + 1) >>> 1
左中位数为第k个: (len + 1) >>> 1
右中位数为第k个: (len + 1 >>> 1) + 1
其实,我们如果明白左中位数为第k个的公式是怎么得出来的,其他的就也都明白了。
既然我们能够计算出左中位数为第k个,那我们如果想计算下标数,只需k - 1即可,即
((len + 1) >>> 1) - 1 -> (len - 1) >>> 1
其实以上说的并不是很容易理解记住,我们在计算中位数的时候可以先尝试举几个例子,然后再写公式。我是通过记住下标的公式,求个数的时候加一即可。
有几点需要注意
可能你会好奇,为什么要根据边界更新来选择中位数,为什么找第一个target和最后一个target可以通过不同的模版实现,下面我们就讲一下这个问题。
二分法的本质是对区间进行操作,每一次查找去掉(一半)无用的区间
如果我们想找到第一个target, 那么我们就应该不断缩小右侧,同时保证target始终在区间中。
我们分开讲这两个过程:缩小、保证
如果想找最后一个target,也是相同的原理,可以自己分析一下
到这里,我们就讲完了为什么找第一个target和最后一个target可以通过不同的模版实现,下面我们讲一下为什么要根据边界更新来选择中位数。
我还闷还是一个找第一个target为例
通过以上,我们可以概括出一句话 左边界负责找到满足条件的范围,右边界负责收缩
我们采取反证法:如果取右中位数,那么考虑只剩两个元素的时候。如果nums[mid]满足条件,那么自然有right=mid,而因为是右中位数,所以right就是mid。显然,此时无法收缩了(所以模版要求这么做 谁=mid + 1就应该用哪个中位数)。
其内在原理是,left和right总是一个负责排除,一个负责收缩。
举个例子,当我们使用left去排除,即left = mid + 1, 那么right必然是收缩 right = mid,这时我们使用左中位数可以保证不管是排除还是收缩,区间都会变小(排除不用说了,肯定会变小;收缩的话至少也会将右中位数收缩掉)。反之,如果使用右中位数,那么收缩的时候区间可能不会变小(即上面说的只剩两个元素的情况)。
所以我们才选择使用左中位数,因为这样右边界才总能收缩到第一个满足条件的元素
现在再来我们试想 即使我们一开始找到的是一个大于target的数字 是不是边界不断收缩 最终找到的还是第一个满足条件的元素。
下面是我总结的一些题目要求及对应的解法