二分法+详解

概述

本文共四种二分法查找值的写法。
按照退出循环的判断条件是否闭合,分为边界闭合式及边界开放式。
而找到对应值后不直接返回,可继续循环找到值的左边界或右边界。

边界闭合式

// 边界闭合式,须注意左右边界两处退出循环要点
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算法公众号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值