【算法与数据结构】【二分搜索】二分搜索的理解以及可以解决的问题

二分搜索

一、 二分搜索的分类

二分搜索不仅仅可以搜索一个值,同时也是搜索左右边界非常有效的手段。实际上二分搜索可以解决6类问题。这些问题包括
1、目标需要插入的位置
2、离目标最近的左侧值
3、离目标最近的右侧值
4、比目标值小的数的个数
5、比目标值大的数的个数
6、目标值的位置

有关搜索左区间和右区间的算法细节本文不再讨论,对这里不了解的同学可以移步拉不拉东的算法小抄。这里我们直接给出最常见的二分搜索算法模板,然后向大家阐明如何快速确立二分算法的细节。

public int search(int[] nums, int target) {
    int left = 0;
    // 真实的左值rightvia表示左端
    int right = nums.length - 1;

    // 当right 大于 left时才退出,即当数组中没有值的时候退出。
    // 搜索区间是[left, right] 闭区间
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            // 下一个搜索区间是 [mid + 1, right]
            left = mid + 1;
        } else if (nums[mid] > target) {
            // 下一个搜索区间是 [left, mid - 1]
            right = mid - 1;
        }
    }
    return -1;
}

int left_bound(int[] nums, int target) {
    int left = 0;
    // 相当于取了超值
    int right = nums.length;

    // 区间为[left, right)
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    if (left == nums.length) return -1;

    return nums[left] == target ? left : -1;
}

int left_bound1(int[] nums, int target) {
    int left = 0;
    int right = nums.length - 1;

    // 区间为[left, right]
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            right = mid - 1;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        }
    }
    if (left == nums.length) return -1;

    return nums[left] == target ? left : -1;
}

int right_bound(int[] nums, int target) {
    int left = 0;
    int right = nums.length;

    // 区间为[left, right)
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] <= target) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }

    // 返回左右并无影响 循环退出时left == right
    if (left - 1 < 0) return -1;
    return nums[left - 1] == target ? (left - 1) : -1;
}

int right_bound1(int[] nums, int target) {
    int left = 0, right = nums.length - 1;

    // 区间为[left, right]
    // 这里循环退出的条件可以等价为 right == left - 1
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid - 1;
        }
    }

    if (left - 1 < 0) {
        return -1;
    }
    return nums[left- 1] == target ? (left - 1) : -1;
}

如何能够快速的写出这个算法,我们需要理解几个概念。
1- 首先我们声明的左右指针代表什么?

实际上左右指针代表着搜索区间,当right = len时,表示区间[left, right)。而当right = len -1时表示区间[left, right]。原因也很简单,对于数组来说len是越界的,所以我们实际的搜索区间就是左闭右开,而当值为len - 1时可以正常去到所以是闭区间。

2- while循环的等号如何确定,左右指针变换如何确认是否需要加减1?

当我们理解了左右指针可以表示区间的概念以后我们就可以很好的理解这个问题了,
对于while退出的条件是搜索完整个区间,对于左闭右开右值不检查故<既满足条件,而两端闭区间左右值都需要检查<=才可以。
对于指针值的确定,只需要记得确定闭区间需要加减1,确定开区间则直接赋值mid即可。

posted @ 2023-01-06 15:16  默默Coding  阅读(203)  评论(0)    收藏  举报