码迷,mamicode.com
首页 > 其他好文 > 详细

leetcode-二分查找相关

时间:2021-04-22 15:54:34      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:元素   改变   搜索   上界   str   位置   bin   第一个   get   

二分查找的基本框架

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

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

首先注意的点:计算 mid 时需要防止溢出。

代码中 left + (right - left) / 2 就和 (left + right) / 2 的结果相同,但是有效防止了 leftright 太大直接相加导致溢出。

TIPS:

加法乘法是否导致超过数据类型范围

浮点型精度在判断是否相等的影响

寻找一个数——基本的二分查找

int binarySearch(int[] nums, int target) {
    int left = 0; 
    int right = nums.length - 1; // 注意,也可以排除size=0情况

    while(left <= right) { //注意
        int mid = left + (right - left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; // 注意
        else if (nums[mid] > target)
            right = mid - 1; // 注意
    }
    return -1;
}
  1. while的循环条件<=,而不是<

前者相当于两端都闭区间 [left, right],后者相当于左闭右开区间 [left, right),因为索引大小为 nums.length 是越界的。

如果为后者,在lef=right时循环结束,则区间内还有一个值未被判断。

关键概念:搜索区间

  1. 为什么 left = mid + 1right = mid - 1

因为 mid 已经搜索过,应该从搜索区间中去除。

  1. 算法的缺陷?

比如说给你有序数组 nums = [1,2,2,2,3]target 为 2,此算法返回的索引是 2,没错。但是如果我想得到 target 的左侧边界,即索引 1,或者我想得到 target 的右侧边界,即索引 3,这样的话此算法是无法处理的。

寻找左侧边界的二分搜索

int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; // 注意
    //1 2 3 4 

    while (left < right) { // 注意
        int mid = (left + right) / 2;//等价
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid; // 注意
        }
    }
    return left;
}
  1. 这里改变了搜索区间的定义:左闭右开
  2. 返回值left可以理解为,小于target的元素个数
  3. 为什么能搜索左边界了?

关键在于对于 nums[mid] == target 这种情况的处理:

if (nums[mid] == target)
        right = mid;

可见,找到 target 时不要立即返回,而是缩小「搜索区间」的上界 right,在区间 [left, mid) 中继续搜索,即不断向左收缩,达到锁定左侧边界的目的。

完整代码:

int left_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    // 搜索区间为 [left, right]
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            // 搜索区间变为 [mid+1, right]
            left = mid + 1;
        } else if (nums[mid] > target) {
            // 搜索区间变为 [left, mid-1]
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 收缩右侧边界
            right = mid - 1;
        }
    }
    // 检查出界情况
    if (left >= nums.length || nums[left] != target)
        return -1;
    return left;
}

寻找右侧边界的二分查找

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

    while (left < right) {
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            left = mid + 1; // 注意
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    return left - 1; // 注意
}

答:类似地,关键点还是这里:

if (nums[mid] == target) {
    left = mid + 1;

nums[mid] == target 时,不要立即返回,而是增大「搜索区间」的下界 left,使得区间不断向右收缩,达到锁定右侧边界的目的。

lower_bound和upper_bound

头文件: <algorithm>

二分查找的函数有 3 个:

lower_bound(起始地址,结束地址,要查找的数值) 返回的是数值 第一个 出现的位置。

upper_bound(起始地址,结束地址,要查找的数值) 返回的是 第一个大于待查找数值 出现的位置。

binary_search(起始地址,结束地址,要查找的数值) 返回的是是否存在这么一个数,是一个bool值

leetcode-二分查找相关

标签:元素   改变   搜索   上界   str   位置   bin   第一个   get   

原文地址:https://www.cnblogs.com/tlam/p/14685762.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!