二分查找

一、前提

二分查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

二、算法步骤

  1. 置查找区间初值,left为0,right为表长-1。

  2. 当left小于等于right时,循环执行以下操作:

    1)middle取值为low和high的中间值;

    2)将给定值target与中间位置记录的关键字进行比较,

    如果target比中间的位置记录的关键字小,则right = middle - 1;

    如果target比中间的位置记录的关键字大,则left = middle + 1;

    若相等则查找成功,返回中间位置middle。

三、循环版-二分查找

    /**
     * 循环版-二分查找
     * @param array 有序数组
     * @param left  左下标
     * @param right 右下标
     * @param target 要查找的元素
     * @return
     */
    public static int binarySearch(int[] array,int target){
        int left = 0;
        int right = array.length - 1;
        while (left <= right){
            int middle = left + ((right - left) >> 1);
            if(array[middle] > target){ //大于
                right = middle - 1;
            }else if(array[middle] < target){ //小于
                left = middle + 1;
            }else { //等于(此情况较少出现,所以放在后面)
                return middle;
            }
        }
        return -1; //找不到
    }

    public static void main(String[] args){
        int[] arr = {1, 2, 3, 4, 5, 6, 7};
        int result = binarySearch(arr,7);
        System.out.println(result);
    }

四、递归版-二分查找

    /**
     * 递归版-二分查找
     * @param array 有序数组
     *  param left  左下标
     *  param right 右下标
     * @param target 要查找的元素
     * @return
     */
    public static int recursiveBSearch(int[] array,int left,int right,int target){
        if(left > right){
            return -1;
        }
        //计算中间角标
        int middle = left + ((right - left) >> 1);

        if(array[middle] > target){//大于
            return recursiveBSearch(array,left,middle - 1,target);
        }else if(array[middle] < target){//小于
            return recursiveBSearch(array,middle + 1,right,target);
        }else { //等于
            return middle;
        }

    }
    
    public static void main(String[] args){
        int[] arr = {1, 2, 3, 4, 5, 6, 7};
        int result = recursiveBSearch(arr,0,arr.length - 1,6);
        System.out.println(result);
    }

四、时间复杂度

二分查找的时间复杂度无非就是while循环的次数

总共有n个元素,

渐渐跟下去就是n,n/2,n/4,....n/2^k,其中k就是循环的次数

由于你n/2^k取整后>=1

即令n/2^k=1

可得k=log2n,(是以2为底,n的对数)

所以时间复杂度为 O(logn)

六、注意

1.中间值得计算有两种方式

方式一:int middle = (left + right) >> 1;

方式二:int middle = left + ((right - left) >> 1);

方式一存在溢出的风险,当left比right大时,有可能会导致middle的值错误。

方式二则可以保证生成的middle一定大于left,小于right。

2.循环执行的条件是 left <= right,而不是left< right,

因为left = right时,查找区间还有最后一个结点,还要进一步比较。

3.int middle = left + ((right - left) >> 1);中的>> 1为右移一位,相当于除以2。

七、演进版

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

    /**
     * 查找第一个等于给定值的元素
     * @param array 有序数组
     * @param target 要查找的元素
     * @return
     */
    public static int firstEqualBSearch(int[] array,int target){
        int left = 0;
        int right = array.length - 1;
        while (left <= right){

            int middle = left + ((right - left) >> 1);

            if(array[middle] > target){ //大于
                right = middle - 1;
            }else if(array[middle] < target){ //小于
                left = middle + 1;
            }else { //等于
                //如果当前为第一个元素或者
                //当前元素的前一个元素不等target,则返回当前角标
                //否则继续查找
                if(middle == 0 || array[middle - 1] != target ){
                    return middle;
                }else {
                    right = middle - 1;
                }
            }
        }
        return -1; //找不到
    }

2、查找第一个大于给定值的元素

    /**
     * 查找第一个大于给定值的元素
     * @param array 有序数组
     * @param target 要查找的元素
     * @return
     */
    public static int firstGreaterBSearch(int[] array,int target){
        int left = 0;
        int right = array.length - 1;
        while (left <= right){

            int middle = left + ((right - left) >> 1);

            if(array[middle] > target){ //大于
                //如果当前是第一个元素,
                //或者当前元素的前一个元素小于等于给定值,则返回
                //否则继续查找
                if (middle == 0 || array[middle-1] <= target){
                    return middle;
                }else {
                    right =  middle - 1;
                }
            }else {
                left = middle + 1;
            }
        }
        return -1; //找不到
    }

3、查找最后一个小于给定值的元素

    /**
     * 查找最后一个小于给定值的元素
     * @param array 有序数组
     * @param target 要查找的元素
     * @return
     */
    public static int firstLessBSearch(int[] array,int target){
        int left = 0;
        int right = array.length - 1;
        while (left <= right){

            int middle = left + ((right - left) >> 1);

            if(array[middle] < target){ //小于
                //如果当前元素的后一个元素大于等于给定值,则返回
                //否则继续查找
                if (array[middle+1] >= target){
                    return middle;
                }else {
                    left =  middle + 1;
                }
            }else {
                right = middle - 1;
            }
        }
        return -1; //找不到
    }
欢迎关注个人公众号,可直接扫描以下二维码或微信搜索“阿毛聊技术”。

你可能感兴趣的:(二分查找)