zoukankan      html  css  js  c++  java
  • 二分查找01.基本二本查找及其变种

    基本的二分查找

    我们假设数据大小是 n,每次查找后数据都会缩小为原来的一半,也就是会除以 2。最坏情况下,直到查找区间被缩小为空,才停止。

    可以看出来,这是一个等比数列。其中 n/2k=1 时,k 的值就是总共缩小的次数。而每一次缩小操作只涉及两个数据的大小比较,所以,经过了 k 次区间缩小操作,时间复杂度就是 O(k)。通过 n/2k=1,我们可以求得 k=log2n,所以时间复杂度就是 O(logn)。

    <?php
    function bsearch($arr,$n){
        $low=0;
        $high=count($arr)-1;
        //注意边界
        while($low<=$high){
    //        $mid=intval(($low+$high)/2);
            $mid=$low+(($high-$low)>>1);
            if($arr[$mid]==$n){
                return $mid;
            }elseif($n<$arr[$mid]){  //n在 [low,mid-1]
                $high=$mid-1;
            }else{  //n 在[mid+1,high]
                $low=$mid+1;
            }
        }
        return -1;
    }
    

    易出错的三点

    1. 循环退出条件

    注意是 low<=high,而不是 low2.mid 的取值

    实际上,mid=(low+high)/2 这种写法是有问题的。
    因为如果 low 和 high 比较大的话,两者之和就有可能会溢出。
    改进的方法是将 mid 的计算方式写成 low+(high-low)/2。
    更进一步,如果要将性能优化到极致的话,我们可以将这里的除以 2 操作转化成位运算 low+((high-low)>>1)。因为相比除法运算来说,计算机处理位运算要快得多。

    3.low 和 high 的更新

    low=mid+1,high=mid-1。 注意这里的 +1 和 -1,如果直接写成 low=mid 或者 high=mid,就可能会发生死循环 比如,当 high=3,low=3 时,如果 a[3] 不等于 value,就会导致一直循环不退出

    递归方式实现

    //递归方式实现
    function bsearch_recursion($arr,$n){
        return bsearch_recursion_internally($arr,0,count($arr)-1,$n) ;
    }
    function bsearch_recursion_internally($arr,$low,$high,$n){
        if($high<$low){
            return -1;
        }
        $mid=$high-intval(($high-$low)>>1);
        if($n==$arr[$mid]){
            return $mid;
        }
        if($n<$arr[$mid]){
            return bsearch_recursion_internally($arr,$low,$mid-1,$n);
        }else{
           return  bsearch_recursion_internally($arr,$mid+1,$high,$n);
        }
        return -1;
    }
    

    二分查找应用场景的局限性

    • 1.二分查找依赖的是顺序表结构,简单点说就是数组。

    那二分查找能否依赖其他数据结构呢?比如链表。
    答案是不可以的,主要原因是二分查找算法需要按照下标随机访问元,数组按照下标随机访问数据的时间复杂度是 O(1),而链表随机访问的时间复杂度是 O(n)。
    所以,如果数据使用链表存储,二分查找的时间复杂就会变得很高。
    二分查找只能用在数据是通过顺序表来存储的数据结构上。如果你的数据是通过其他数据结构存储的,则无法应用二分查找。

    • 2.二分查找针对的是有序数据
    • 3.数据量太小不适合二分查找

    如果要处理的数据量很小,完全没有必要用二分查找,顺序遍历就足够了。
    比如我们在一个大小为 10 的数组中查找一个元素,不管用二分查找还是顺序遍历,查找速度都差不多。只有数据量比较大的时候,二分查找的优势才会比较明显。
    不过,这里有一个例外。如果数据之间的比较操作非常耗时,不管数据量大小,我都推荐使用二分查找。
    比如,数组中存储的都是长度超过 300 的字符串,如此长的两个字符串之间比对大小,就会非常耗时。我们需要尽可能地减少比较次数,而比较次数的减少会大大提高性能,这个时候二分查找就比顺序遍历更有优势。

    • 4.数据量太大也不适合二分查找

    二分查找的底层需要依赖数组这种数据结构,而数组为了支持随机访问的特性,要求内存空间连续,对内存的要求比较苛刻。
    比如,我们有 1GB 大小的数据,如果希望用数组来存储,那就需要 1GB 的连续内存空间

    • 1.查找第一个值等于给定值的元素
    • 2.查找最后一个值等于给定值的元素
    • 3.查找第一个大于等于给定值的元素
    • 4.查找最后1个小于等于给定值的元素

    变形二分查找

    1.查找第一个值等于给定值的元素

    关键点在于在找到等于的值后,还继续怎么找

    //第1个等于给定值
    function bsearch_first_eq($arr,$value){
        $low=0;
        $high=count($arr)-1;
    
        while($high>=$low){
            $mid=$low+(($high-$low)>>1);
            if($arr[$mid]==$value){
                //因为是要找第一个,所以是从mid往前找,
                if($mid==0 ||$arr[$mid-1]!=$value){
                    return $mid;
                }
                $high=$mid-1;
            }else if($arr[$mid]>$value){
                $high=$mid-1;
            }else{
                $low=$mid+1;
            }
    
        }
        return -1;
    }
    
    

    2.查找最后一个值等于给定值的元素

    function bsearch_last_eq($arr,$value){
        $low=0;
        $high=count($arr)-1;
    
        while($high>=$low){
            $mid=$low+(($high-$low)>>1);
    
            if($arr[$mid] ==$value){
                //因为要找最后1个,需要往后找
                if($mid == $high || $arr[$mid+1] !=$value){
                    return $mid;
                }
                $low=$mid+1;
    
            }elseif($arr[$mid]<$value){
                $low=$mid+1;
            }else{
                $high=$mid-1;
            }
    
        }
        return -1;
    }
    

    3.查找第一个大于等于给定值的元素

    function bsearch_first_egt($arr,$value){
        $low=0;
        $high=count($arr)-1;
    
        while($high>=$low){
            $mid=$low+(($high-$low)>>1);
            if($arr[$mid]>=$value){
                if($mid==0 ||$arr[$mid-1]<$value){
                    return $mid;
                }
                $high=$mid-1;
    
            }else{
                $low=$mid+1;
            }
    
    
        }
    
        return -1;
    }
    

    4.查找最后1个小于等于给定值的元素

    function  bsearch_last_elt($arr,$value){
        $low=0;
        $high=count($arr)-1;
    
        while($high>=$low){
            $mid=$low+(($high-$low)>>1);
    
            if($arr[$mid]<=$value){
                if($mid==$high||$arr[$mid+1]>$value){
                    return $mid;
                }
    
                $low=$mid+1;
    
            }else{
                $high=$mid-1;
            }
        }
    
        return -1;
    }
    
  • 相关阅读:
    对象形式传递
    解决DLNA方案的技术框架
    关于DLNA
    MAC配置Xcode的Cocos2d-x环境
    什么叫做双缓冲?
    Window7 Cocos2d-x配置开发环境
    Windows 8.1 Update 2更新了什么?
    微软发布Windows Phone 8.1 Update 和中文版Cortana“小娜”
    大开眼界 游览Facebook香港办公室
    小米的“假照片”危机
  • 原文地址:https://www.cnblogs.com/HKUI/p/13844512.html
Copyright © 2011-2022 走看看