对于二分查找的理解

823 阅读1分钟

自我理解的而二分查找竟然是错的?

对于二分查找,相信大家都十分的熟悉,我之前理解的二分查找一直是这个样子的

private static int binarySearch(int[] test, int val) {
    int left = 0, right = test.length - 1;
    while (left < right) {
        int mid = (right - left) / 2 + left;
        if (val > test[mid]) left = mid + 1;
        else if (val < test[mid]) right = mid;
        else return mid;
    }
    return left;
}

首先有两个细节说明一下:

  1. 设置中间值 mid 的时候,正常逻辑采取的方式为 (left + right) / 2 ,但是当 leftright 过大时,会导致数字大小越界。为了避免不必要的内存空间的占据,可以使用 (right - left) / 2 + left 这样的方式求中间值。
  2. 在不满足第一个条件 val > test[mid] 时,left = mid + 1,而为什么第二个条件时就没有选择 right = mid - 1 呢?这是要分情况讨论的点。如果在条件 while (left < right) 这样的情况下,在计算 mid 的时候就已经默认的去掉后缀的小数,从而缩小了 right 的值;再另一种情况下, while (left <= right) 的情况下,这个时候为了避免死循环,right = mid - 1 ,就必须这样设置了。 相信上面两个点大家应该很清楚,接下来说下我最初理解的二分查找是什么样的。 最初我理解的二分法,

image.png

如果说要寻找的目标是4个的话,那按照上述代码的过程,只需要执行一次,找到中点4返回就可以了,对了简单的二分法查找来说,这样效率很高。接下来我想说说我对二分法新的理解,同样是上面的简单查找


private static int binarySearch(int[] test, int val) {
    int left = 0, right = test.length - 1;
    while (left < right) {
        int mid = (right - left) / 2 + left;
        if (val > test[mid]) left = mid + 1;
        else right = mid;
    }
    return left;
}

1651740203(1).png

代码的执行过程可以看作是这个样子,左右的边界一直往中间靠拢,直到不满足 while (left < right) 这样的条件为止,最后的返回值为 left 。这样算法的复杂度一直是 logn,执行的流程是长了,但是给人的代码的清爽感觉和左右向中间逼近的思想就很明确。在做一些复杂的二分查找的题目的时候,不妨试试这样的写法,让逼近过程的条件更加的简化,有助于思考和推理。
我个人觉得上述两种方法的区别在于,一种是以找到目标值为目的的,只要找到目标值,即返回;另一个倾向于左右逼近,就算找到了目标值,依旧左右逼近的思想。