十分好用的二分查找模板
2019-10-28 星期一 开始吧
最近发现一个二分查找很好用的模板,花了一点时间理解了下这个模板,然后这两天就会一直去找二分查找的题,利用那套模板来实现。这套模板和传统模板的不同在于传统的模板,可能我们会这样写:
function binarySerach($data, $res)
{
$left = 0;
$right = count($data) - 1;
while ($left <= $right) {
$middle = $left + (($right - $left) >> 1);
if ($data[$middle] == $res) return $middle;
else if ($data[$middle] > $res) $right = $middle - 1;
else $left = $middle + 1;
}
return -1;
}
题目描述
题目让我们找出重复的数,给定的数组的值都在1-n之间,数组总数是n+1,那么必然有数字是重复的,让我们来找出这个重复的数。
题目分析
/**
* @param Integer[] $nums
* @return Integer
*/
function findDuplicate($nums) {
$left = 1;
$right = count($nums) - 1;
while ($left < $right) { // <
$middle = ($left + $right) >> 1;
$sum = 0;
for ($j = 0; $j < count($nums); $j++) {
if ($nums[$j] <= $middle) {
$sum++;
}
}
if ($sum <= $middle) { //排除掉中位数
$left = $middle + 1;
} else { //不能排除中位数
$right = $middle;
}
}
//相返回left还是right都可以 因为必然存在left==right
return $left;
}
// $middle=floor(($l+$r)/2);
// $middle=$l+floor(($r-$l)/2);
// $middle=$l+(($r-$l)>>1);
// $middle = ($l + $r) >> 1;
//上面的结果都是一样的,只是在数值大的情况下值会溢出
[1,3,6,9,12] 奇数的时候中位数没有争议直接是6 (0+4)/2 位置2=6
[1,3,6,9] 如果是($l + $r) >> 1 最终取的是左中位3 也就是位置1
[1,3,6,9] 想要右中位那么($l + $r + 1) >> 1 取6
[1,3,6,9]
就像现在这样
// $middle = ($l + $r) >> 1; 此时中位数是3
if(排除中位数的语句){
$right = $miidle+1
}else{
$left=$middle;
}
//这时候对于如果走进的是else,就是灾难,因为left并不收缩,
//这时候就进入了死循
/**
* @param Integer $x
* @return Integer
*/
function mySqrt($x)
{
$left = 0;
$right = $x;
while ($left < $right) {
$middle = ($left + $right + 1) >> 1;
// $middle=($left+$right)>>1;
if ($middle * $middle > $x) {
$right = $middle - 1;
} else {
$left = $middle;
}
}
return $left;
}
所以什么时候知道取哪边呢,也很简单,你可以先随便取左或者右,在收缩边界的时候,第一个语句直接写能排除中位数的,那么另一个边界一定不能排除中位数。然后再去判断取的那边中位数在边界的条件下是否能收缩空间,如若不能,反向获取中位数。当然这里的二分场景并不是很复杂,到了更复杂的二分场景,可能还需要做更多的操作。