零 必读系列 5二分查找解题套路框架

目录

一、二分查找框架

二、寻找一个数

1. 为什么是 while(left <= right), 而不是 while(left < right)

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

3. 此算法缺陷

三、寻找左侧边界或右侧边界


一、二分查找框架

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 ...;
}

二、寻找一个数

int binarySearch(int[] nums, int target) {
    int left = 0; 
    int right = nums.length - 1; // 注意

    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), 而不是 while(left < right)

因为 right 初值是 nums.lenght - 1,即最后一个数的索引。搜索区间为 [left, right] 双闭区间。

  • left <= right 的终止条件是 left == right + 1, 终止搜索区间为 [right + 1, right] 。
  • left < rihgt 的终止条件是 left == right, 终止搜索区间为 [right, right] 。区间非空,right 这个值还没被搜索,因此不合理。

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

因为 mid 已被搜索过,下次搜索 [mid + 1, right] 或 [left, mid - 1]。

3. 此算法缺陷

nums = [1,2,2,2,3] 这种找2,会返回索引2,不会找到边界索引1或者3。

三、寻找左侧边界或右侧边界

三种写在一起便于比较理解:

int binary_search(int[] nums, int target) {
    int left = 0, right = nums.length - 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 if(nums[mid] == target) {
            // 直接返回
            return mid;
        }
    }
    // 直接返回
    return -1;
}

int left_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 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 if (nums[mid] == target) {
            // 别返回,锁定左侧边界
            right = mid - 1;
        }
    }
    // 最后要检查 left 越界的情况
    if (left >= nums.length || nums[left] != target)
        return -1;
    return left;
}


int right_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 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 if (nums[mid] == target) {
            // 别返回,锁定右侧边界
            left = mid + 1;
        }
    }
    // 最后要检查 right 越界的情况
    if (right < 0 || nums[right] != target)
        return -1;
    return right;
}

寻找左右边界:

  1. 就在 nums[mid] == target 时不直接返回,例如,寻找左边界时让 right = mid - 1,使区间不断向左收缩。
  2. 最后要检测数值越界情况。

猜你喜欢

转载自blog.csdn.net/weixin_36389889/article/details/105214798