查找算法之二分查找法

查找算法之二分查找法

思想

二分查找法的思想很是简单,对于一个有序数列,找它中间的元素,看是不是查找目标,若是不是,就看这个查找目标是小于仍是大于中间元素,而后在对应的区间内重复上述过程。java

算法

须要注意几个问题:git

  • while 循环:while 循环的条件应该是 left < right 仍是 left <= right 呢?github

    • 这能够从咱们设置的左右边界判断出来,咱们设置的 left = 0, right = n - 1,所以 [left, right] 是一个闭区间,那么当 left = right 时,[left, right] 区间一样知足咱们的设置,所以,这个循环内应该是 left <= right。
  • target 和 arr[mid] 的判断:当 target > arr[mid] 时,是应该 left = mid 仍是 left = mid + 1 呢?算法

    • 这和在 while 中的判断是一个思路,当 target > arr[mid] 时,target 在 [mid + 1, r] 中,而非 [mid, r],由于显然此时 mid 已经不可能等于 target 了,所以咱们不须要再比较 target 和 arr[mid]。
    • 一样地,当 target < arr[mid] 时,也是相似的判断。
  • 循环不变量:left 和 right 的定义十分重要,由于在后面咱们要不断地维护这个定义,咱们必需要保证是在 [left, right] 这个区间里寻找 target,这也就是 循环不变量,意思就是 left 和 right 的值虽然一直在变化,可是有一个声明 在 [left, right] 区间内寻找 target 是永远不变的,只要咱们维护住这个 循环不变量,那么就能够保证咱们的算法是正确的。固然,这里你也能够定义成 left = 0, right = n,即[left, right),那么此时定义就发生了变化,相应地,在 while 中为了维护这个定义,咱们须要把条件改为 left < right,由于当 left = right 时,显然 [left, right)是错误的,后面再查找时也要作相应的修改。
private int binarySearch(T arr[], int n, T target) {

        int left = 0, right = n - 1; // 在 [left, right] 区间内寻找 target
        
        while (left <= right) { // 当 left = right 时,区间 [left, right] 仍然有效
            int mid = (left + right) / 2;
            if (arr[mid] == target)
                return mid;
            if (target > arr[mid])
                left = mid + 1; // target 在 [mid+1, r] 中
            else
                right = mid - 1; // target 在 [left, mid - 1] 中
        }

        return -1;
    }

不知道到这里你们有没有发现一个 bug 测试

由于 left 和 right 都是 int,因此当值足够大时,在计算 mid = (left + right) / 2 时可能会发生整型溢出!code

所以,为了不这个问题,咱们使用减法来计算。get

彻底正确的二分查找法it

public int binarySearch(T[] arr, int n, T target) {

        int left = 0, right = n - 1; // 在 [left, right] 区间内寻找 target

        while (left <= right) { // 当 left = right 时,区间 [left, right] 仍然有效
            int mid = left + (right - left) / 2;
            if (arr[mid].compareTo(target) == 0)
                return mid;
            if (target.compareTo(arr[mid]) > 0)
                left = mid + 1; // target 在 [mid+1, r] 中
            else
                right = mid - 1; // target 在 [left, mid - 1] 中
        }

        return -1;
    }

测试

咱们能够写一个 Util 来帮助咱们生成测试用例io

ArrayUtil.javaast

public static Integer[] generateSortedArray(int n) {

        assert n > 0;

        Integer[] arr = new Integer[n];
        for (int i = 0; i < n; i++) {
            arr[i] = i;
        }

        return arr;
    }

BinarySearch.java

public static void main(String[] args) {

        BinarySearch<Integer> bs = new BinarySearch<Integer>();
        int n = (int)Math.pow(10, 7); // 用 10,000,000 数据测试
        Integer[] data = ArrayUtil.generateSortedArray(n);

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < n; i++)
            if (i != bs.binarySearch(data, n, i))
                throw new IllegalStateException("find i failed");
        long endTime = System.currentTimeMillis();

        System.out.println("Binary Search success!");
        System.out.println("Time cost: " + (endTime - startTime) + "ms");
    }

完整代码:github: My-Notes/algorithm(Java)/01-binarySearch/src/

相关文章
相关标签/搜索