概述
本文共四种二分法查找值的写法。
按照退出循环的判断条件是否闭合,分为边界闭合式及边界开放式。
而找到对应值后不直接返回,可继续循环找到值的左边界或右边界。
边界闭合式
// 边界闭合式,须注意左右边界两处退出循环要点
function binarySearch($nums, $target)
{
if ($nums == []) {
return -1;
}
$firstIndex = 0;
$lastIndex = count($nums) - 1;
while ($firstIndex <= $lastIndex) {
// 若$nums长度是奇数,得到的$middleIndex为左半区最大值
// 若$nums长度是偶数,得到的$middleIndex正好为中间值;若到左边界处则为左边界值
$middleIndex = intval(($firstIndex + $lastIndex) / 2);
if ($target == $nums[$middleIndex]) {
return $middleIndex;
}
// 左右边界闭合式,$middleIndex值已判断,此时左移或右移应再偏一步
if ($target < $nums[$middleIndex]) {
$lastIndex = $middleIndex - 1;
} else {
$firstIndex = $middleIndex + 1;
}
}
return -1;
}
边界开放式
// 边界开放式
function binarySearch2($nums, $target)
{
if ($nums == []) {
return -1;
}
$firstIndex = 0;
$lastIndex = count($nums);
while ($firstIndex < $lastIndex) {
// 若$nums长度是奇数,得到的$middleIndex正好为中间值
// 若$nums长度是偶数,得到的$middleIndex为右半区最小值;若到左边界处则为左边界值
$middleIndex = intval(($firstIndex + $lastIndex) / 2);
if ($target == $nums[$middleIndex]) {
return $middleIndex;
}
if ($target < $nums[$middleIndex]) {
// 边界开放式二分时,$middleIndex为中间值或右半区最小值;
// 若是中间值时-1,会遗漏左半区某些值的判断;
// 不减1也与初始化时$lastIndex = count($nums)形式对称而不断while循环
$lastIndex = $middleIndex;
} else {
$firstIndex = $middleIndex + 1;
}
}
return -1;
}
求符合条件的右边界
// 求符合条件的右边界
function binarySearch4($nums, $target)
{
if ($nums == []) {
return -1;
}
$firstIndex = 0;
// 效果同下
$lastIndex = count($nums) - 1;
while ($firstIndex <= $lastIndex) {
$middleIndex = intval(($firstIndex + $lastIndex) / 2);
if ($target == $nums[$middleIndex]) {
// 不返回,$middleIndex+1以逼近求符合条件的右边界;直到$firstIndex>$lastIndex循环退出
$firstIndex = $middleIndex + 1;
} elseif ($target < $nums[$middleIndex]) {
$lastIndex = $middleIndex - 1;
} else {
$firstIndex = $middleIndex + 1;
}
}
// // 效果同上
// $lastIndex = count($nums);
// while ($firstIndex < $lastIndex) {
// $middleIndex = intval(($firstIndex + $lastIndex) / 2);
// if ($target == $nums[$middleIndex]) {
// // 不返回,$middleIndex+1以逼近求符合条件的右边界;直到$firstIndex>=$lastIndex循环退出
// $firstIndex = $middleIndex + 1;
// } elseif ($target < $nums[$middleIndex]) {
// $lastIndex = $middleIndex;
// } else {
// $firstIndex = $middleIndex + 1;
// }
// }
// $firstIndex加多了得减回来
$firstIndex--;
return isset($nums[$firstIndex]) && $nums[$firstIndex] == $target ? $firstIndex : -1;
}
求符合条件的左边界
// 求符合条件的左边界
function binarySearch5($nums, $target)
{
if ($nums == []) {
return -1;
}
// 效果同上
$firstIndex = 0;
$lastIndex = count($nums) - 1;
while ($firstIndex <= $lastIndex) {
$middleIndex = intval(($firstIndex + $lastIndex) / 2);
if ($target == $nums[$middleIndex]) {
// $middleIndex-1,继续逼近求符合条件的左边界
$lastIndex = $middleIndex - 1;
} elseif ($target < $nums[$middleIndex]) {
$lastIndex = $middleIndex - 1;
} else {
$firstIndex = $middleIndex + 1;
}
}
// $lastIndex减多了加回来
$lastIndex++;
// // 效果同上
// $firstIndex = 0;
// $lastIndex = count($nums);
// while ($firstIndex < $lastIndex) {
// $middleIndex = intval(($firstIndex + $lastIndex) / 2);
// if ($target == $nums[$middleIndex]) {
// // 继续逼近求符合条件的左边界
// $lastIndex = $middleIndex;
// } elseif ($target < $nums[$middleIndex]) {
// $lastIndex = $middleIndex;
// } else {
// $firstIndex = $middleIndex + 1;
// }
// }
// // 注意此处无须 $lastIndex++;
return isset($nums[$lastIndex]) && $nums[$lastIndex] == $target ? $lastIndex : -1;
}
调试
$nums = [0, 1, 2, 3, 4];
$targets = [-1, 0, 1, 2, 3, 4, 5];
foreach ($targets as $target) {
echo binarySearch($nums, $target), ';';
}
echo ' ';
$nums = [0, 1, 2, 3, 4, 5];
$targets = [-1, 0, 1, 2, 3, 4, 5, 6];
foreach ($targets as $target) {
echo binarySearch($nums, $target), ';';
}
echo ' ';
$nums = [0, 1, 2, 2, 2, 5, 6];
$targets = [-1, 0, 1, 2, 2, 2, 5, 6, 7];
foreach ($targets as $target) {
echo binarySearch($nums, $target), ';';
}
// 使用前两种写法,会输出-1;0;1;3;3;3;5;6;-1;
// 而使用后两种写法,会输出-1;0;1;4;4;4;5;6;-1;或-1;0;1;2;2;2;5;6;-1;
echo ' ';
$nums = [0, 1, 2, 2, 4, 5, 5, 7];
$targets = [-1, 0, 1, 2, 2, 4, 5, 5, 7, 8];
foreach ($targets as $target) {
echo binarySearch($nums, $target), ';';
}
echo ' ';
$nums = [0, 1, 2, 2, 2, 5, 6, 6, 6, 9, 10];
$targets = [-1, 0, 1, 2, 2, 2, 5, 6, 6, 6, 9, 10, 11];
foreach ($targets as $target) {
echo binarySearch($nums, $target), ';';
}
echo "\n";
参考
labuladong算法公众号