是什么

概念

二分法(Bisection method) 即一分为二的方法,又叫折半查找方法。
把一组有序数列分为左右两部分,从这组数字的中间位置开始找:
如果中间位置的数等于目标数,则直接返回;
如果中间位置的数大于目标数,则从左边部分查找;
如果小于目标数,则从右边部分查找;
重复以上过程,直到找到满足条件的记录,使查找成功。

场景:从一堆有序的数中,找出一个数;当数据量很大适宜采用该方法。

前提

注意前提是 有序的数列

思想

分治

复杂度

时间复杂度:
都是O(log2 N)

空间复杂度:
非递归方式: 空间复杂度是O(1);
递归方式: 空间复杂度:O(log2N )

递归需要开辟额外的空间辅助查询。

实现

1. 递归方式

public static int binarySearchRecursive(int[] arr, int low, int high, int key) {
        //边界条件:如果目标数没有在数组范围内(即比最左边的数小,比最右边的数大)
        if (arr == null || arr.length == 0 || arr[low] > key || arr[high] < key || low > high) {
            return -1;
        }
        
        // 获取中间位置下标
        int mid = (low + high) / 2;
        // 将中间位置的数和目标数作比较,如果中间位置的数等于目标数,则直接返回下标,
        // 如果中间位置的数大于目标数,则将左边部分用递归方法继续查找;如果小于目标数,则从右边部分用递归方法继续查找
        if (arr[mid] == key) {
            return mid;
        } else if (arr[mid] > key) {
            return binarySearch(arr, low, mid - 1, key);
        } else {
            return binarySearch(arr, mid + 1, high, key);
        }
    }

// 测试下:从一组数中找3,输出数组下标
 public static void main(String[] args) {
        int[] arr = {2, 3, 5, 7, 9, 78, 90, 167};
        System.out.println(binarySearchRecursive(arr, 0, (arr.length) - 1, 3));
 }

结果:

java 对数据二分法 java中的二分法_java 对数据二分法

2.非递归方式:while 循环

public static int binarySearch(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;
        while (low <= high) {
            int middle = (low + high) / 2;
            if (key < arr[middle]) {
                high = middle - 1;
            } else if (key > arr[middle]) {
                low = middle + 1;
            } else {
                return middle;
            }
        }
        return -1;

    }
    
    // 测试下:从一组数中找3,输出数组下标
     public static void main(String[] args) {
        int[] arr = {2, 3, 5, 7, 9, 78, 90, 167};
        System.out.println("数组下标:"+binarySearch(arr, 3));
    }

结果:

java 对数据二分法 java中的二分法_java_02

优缺点

优点: 比较次数少,查找速度快,平均性能好;

缺点:要求待查表为有序表,插入删除困难。

适用于不经常变动而查找频繁的有序列表。

应用

java中java.util包下的Arrays类中的binarySearch 方法:
java.util.Arrays 中的binarySearch

private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                                     int key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; 
        }
        return -(low + 1);  
    }
为什么用  (low + high) >>> 1 代替(low + high) /2

使用(low + high) >>> 1求平均值仅适用于结果保证不是负数的情况,但好处是即使两个数的和int溢出也能求出正确的结果。

用(low + high) >>> 1代替(low + high) /2是非常正确的,首先是因为数组下标肯定不会是负数,另一方面如果low + high大于int最大值时,只有>>>1能保证结果正确。

优化 :
前面 二分法的两种实现 可以优化 为 用“ >>> 1” 右移 代替 " / 2 " .