常用算法PHP版(一)- 二分查找

为什么要学算法?当然是为了装13呀,233。好吧,其实是因为最近测试并发的感受。

当代码能够缩短运行时间,运行次数,就可以给项目节省硬件的钱了。什么??你说硬件的钱关你屁事?老弟,你要这样想,这事关系到老板,也就关系到技术负责人。技术负责人不能让老板发现他带的团队,写的代码运行效率低下。所以他面你的时候需要你懂算法,并不是为了装13。

同时的,机器多了,需要招运维。每年要多租很多台服务器。老板只是把花在这些上面的一部分换成了你的薪资。

当应用响应缓慢,丢失用户也是一件损失很大的事情。

二分查找
上面的是二分查找,下面的是顺序查找,都是查找1,看上面的即可。
在这里插入图片描述
通过动态图,你就可以知道二分查找的核心就是每一次运行都是找最中间的值,去判断是否大于要查找的值。

往左,查找1

/**
 * 二分查找
 *
 * 假设数据是按升序排序的,代码中注释的数值为第一轮查找
 * @param array $arr    一个有十个数字的数组
 * @param int   $search 搜索值为1
 * @return bool|int
 */
function twoPoints($arr, $search)
{
    $l = 0; // 最左为0
    $r = count($arr) - 1; // 最右9

    while($l <= $r) { // 当0小于等于9

        $mid = floor(($l + $r) / 2); // 向下取整,获取中值,为4

        if ($arr[$mid] == $search) { // 如果中值等于要搜索的值,就直接返回了
            return $mid;
        } elseif($arr[$mid] > $search) { // 如果数组的下标[4]的值(4)大于要搜索的值(1)
            $r = $mid - 1; // 将下标减1,使下标中的数值[4]变为[3]并赋给最右的变量。------ 此时$arr[中值-1=3],最右的下标值为3,意味着第一轮的结果:$arr = [0, 1, 2, 3]
        } elseif ($arr[$mid] < $search) {
            $l = $mid + 1;
        }
    }

    return false; // 所要查找的值,数组中不存在
}

$arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
$result = twoPoints($arr, 1); // 返回值是下标键
echo $arr[$result];

while($l <= $h) 为什么要小于等于?因为如果你只是小于,搜索10的话,条件不成立就不会进入循环。
floor(($h + $l) / 2) 为什么要向下取整?因为单数的话除以2就不为整数。作为下标用的,虽然不加取整也可以,但是其它的语言就不一定了。

第二轮查找

/**
 * 二分查找
 *
 * 此时,$arr = [0, 1, 2, 3],$l = 0,$r = 3
 */
function twoPoints($arr, $search)
{
    /*
       无视即可,这里讲的是while循环,不是一个新的方法,现在第二轮仍在循环内
        $l = 0; // 最左为0
        $r = count($arr) - 1; // 最右9
    */
    while($l <= $r) { // 当0小于等于3

        $mid = floor(($l + $r) / 2); // 向下取整,获取中值,为1

        if ($arr[$mid] == $search) { // 如果中值等于要搜索的值,就直接返回了,1==1
            return $mid;
        } elseif($arr[$mid] > $search) {
            $r = $mid - 1;
        } elseif ($arr[$mid] < $search) {
            $l = $mid + 1;
        }
    }

    return false;
}

往右,查找8

<?php
/**
 * 二分查找
 *
 * 假设数据是按升序排序的,代码中注释的数值为第一轮查找
 * @param array $arr    一个有十个数字的数组
 * @param int   $search 搜索值为8
 * @return bool|int
 */
function twoPoints($arr, $search)
{
    $l = 0; // 最左为0
    $r = count($arr) - 1; // 最右9

    while($l <= $r) { // 当0小于等于9

        $mid = floor(($l + $r) / 2); // 向下取整,获取中值,为4

        if ($arr[$mid] == $search) { // 如果中值等于要搜索的值,就直接返回了
            return $mid;
        } elseif($arr[$mid] > $search) { // 如果数组的下标[4]的值(4)大于要搜索的值(8),此轮不大于,所以往下看
            $r = $mid - 1;
        } elseif ($arr[$mid] < $search) { // 否则如果数组的下标[4]的值(4)小于要搜索的值(8)
            $l = $mid + 1; // 将下标加1,使下标中的数值[4]变为[5]并赋给最左的变量。------ 此时$arr[中值+1=5],最左的下标值为5,意味着第一轮的结果:$arr = [5, 6, 7, 8, 9]
        }
    }

    return false; // 所要查找的值,数组中不存在
}

$arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
$result = twoPoints($arr, 8); // 返回值是下标键
echo $arr[$result];

第二轮循环

/**
 * 二分查找
 *
 * 此时,$arr = [5, 6, 7, 8, 9],$l = 5,$r = 9
 */
function twoPoints($arr, $search)
{
    /*
        无视即可,这里讲的是while循环,不是一个新的方法,现在第二轮仍在循环内
        $l = 0; // 最左为0
        $r = count($arr) - 1; // 最右9
     */


    while($l <= $r) { // 最左满足小于等于最右,5<=9

        $mid = floor(($l + $r) / 2); // (5+9)/2=7,中值为7

        if ($arr[$mid] == $search) { // 如果中值等于要搜索的值,就直接返回了
            return $mid;
        } elseif($arr[$mid] > $search) {
            $r = $mid - 1;
        } elseif ($arr[$mid] < $search) { // 否则如果数组的下标[7]的值(7)小于要搜索的值(8)
            $l = $mid + 1; // 将下标加1,使下标中的数值[7]变为[8]并赋给最左的变量。------ 此时$arr[中值+1=8],最左的下标值为8,意味着第二轮的结果:$arr = [8, 9]
        }
    }

    return false;
}

第三轮循环

/**
 * 二分查找
 *
 * 此时,$arr = [8, 9],$l = 8,$r = 9
 */
function twoPoints($arr, $search)
{
    /*
        无视即可,这里讲的是while循环,不是一个新的方法,现在第三轮仍在循环内
        $l = 0; // 最左为0
        $r = count($arr) - 1; // 最右9
     */


    while($l <= $r) { // 最左满足小于等于最右,8<=9

        $mid = floor(($l + $r) / 2); // (8+9)/2=8,中值为8

        if ($arr[$mid] == $search) { // 如果中值等于要搜索的值,就直接返回了,8==8
            return $mid;
        } elseif($arr[$mid] > $search) {
            $r = $mid - 1;
        } elseif ($arr[$mid] < $search) {
            $l = $mid + 1;
        }
    }

    return false; 
}

简化

/**
 * 二分查找
 *
 * @param array $arr    一个有十个数字的数组
 * @param int   $search 搜索值为8
 * @return bool|int
 */
function twoPoints($arr, $search, $left, $right)
{
    while($left <= $right) {

        $mid = floor(($left + $right) / 2);

        if ($arr[$mid] == $search) {
            return $mid;
        } elseif($arr[$mid] > $search) {
            $right = $mid - 1;
        } elseif ($arr[$mid] < $search) {
            $left = $mid + 1;
        }
    }

    return false;
}

$arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
$result = twoPoints($arr, 8, 0, count($arr) -1);
echo $arr[$result];

递归的写法
上面的left和right是为了好理解,其实应该理解为最高的键值与最低的键值比较恰当,毕竟是比大小嘛。

/**
 * @param array    $arr      数组
 * @param int      $search   要搜索的值
 * @param int      $low      最低位
 * @param int      $top      最高位
 * @return bool|int
 */
function twoPoints($arr, $search, $low, $top)
{
    if ($low <= $top) {
        $mid = floor(($low + $top) / 2);

        if ($arr[$mid] == $search) {
            return $mid;
        } elseif ($arr[$mid] <= $search) {
            return twoPoints($arr, $search,$mid+1, $top);
        } else {
            return twoPoints($arr, $search,$low, $mid-1);
        }
    } else {
        return false;
    }

}

$arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
$result = twoPoints($arr, 8,  0, count($arr) -1);
echo $arr[$result];

与上面不同的是:递归时,要传最低位或最高位。

那么,为什么要用二分查找?
如果让你在$arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];中查找8,最简单的怎么做?

顺序查找

<?php
$arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
$search = 8;

foreach ($arr as $value) {
    if ($value == $search) {
        echo $value;
    }
}

但是,现在有个问题。这里遍历了九次才找到8,而我们上面的二分查找只用了三次。差别只有六次嘛,那换成亿次呢?

总结:
1、当中值小于要搜索的数值,那么中值+1。
2、取(中值+1的下标键+最高下标键)÷ 2 向下取整,得出新的中值。如果新的中值等于要搜索的值就返回,否则重复1。

3、当中值大于要搜索的数值,那么中值-1。
4、取(中值-1的下标键+最低下标键)÷ 2 向下取整,得出新的中值。如果新的中值等于要搜索的值就返回,否则重复3。

当每次获取一个区间时,区间外的数值就被排除了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值