欢迎光临
我们一直在努力

【算法拾光】二分查找|二分查找 + 再排序数组中查找元素的第一个和最后一个位置

标签:二分查找、左边界二分、右边界二分、LeetCode 704 / LeetCode 34 区间规范:统一使用 左闭右闭 [left, right] 写法,工程、面试最稳,减少边界 bug


一、LeetCode 704 二分查找704. 二分查找 – 力扣(LeetCode)

1. 题目分析

题目:给定一个升序排列、无重复元素的整型数组 nums 和目标值 target。如果数组中存在目标值,返回它的下标;不存在则返回 -1。 已知条件:数组有序、元素不重复 需求:快速定位目标,不暴力遍历 暴力缺点:O (n),数据量大时很慢;二分可以做到 O (log n)

2. 算法原理(分步)

二分核心思想:有序数组可以不断折半,排除一半不可能的区间

  • 定义查找区间 [left, right],左端点 left = 0,右端点 right = nums.size()-1
  • 循环条件:left <= right(左闭右闭,区间有效才循环)
  • 取中间下标 mid = left + (right – left) / 2,规避 left+right 溢出
  • 比较 nums[mid] 和 target:
    • nums[mid] == target:找到目标,直接返回 mid
    • nums[mid] < target:目标在右边区间,更新 left = mid + 1(mid 已经比对过,不需要再包含)
    • nums[mid] > target:目标在左边区间,更新 right = mid – 1
  • 循环结束,区间空了,说明不存在 target,返回 – 1
  • 3. 代码编写

    class Solution {
    public:
    int search(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() – 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{
    right = mid – 1;
    }
    }
    return -1;
    }
    };


    二、LeetCode 34 在排序数组中查找元素的第一个和最后一个位置34. 在排序数组中查找元素的第一个和最后一个位置 – 力扣(LeetCode)

    1. 题目分析

    题目:升序整型数组(允许元素重复),给目标值 target,找出目标在数组里第一次出现的下标 和 最后一次出现的下标;不存在返回 [-1, -1] 难点:数组有重复!普通二分找到一个就退出,无法拿到左右边界。 举例:nums = [5,7,7,8,8,10], target=8,答案是 [3,4] 需求拆解:

    • 子问题 1:二分找左边界:等于 target 的最靠左位置
    • 子问题 2:二分找右边界:等于 target 的最靠右位置

    2. 算法原理(分步)

    依然使用左闭右闭 [left, right],和 704 保持一套逻辑,方便记忆

    找左边界逻辑
  • 初始化 left=0,right=nums.size()-1,答案 res=-1(默认不存在)
  • 循环 left <= right
  • 算出 mid,对比:
    • nums[mid] == target:先记录当前 mid 为候选答案,但不返回!继续向左搜索,尝试找更早出现的 target → right = mid – 1
    • nums[mid] < target:目标在右侧 → left = mid + 1
    • nums[mid] > target:目标在左侧 → right = mid – 1
  • 循环结束,res 就是左边界,没找到维持 – 1
  • 找右边界逻辑
  • 初始化 left=0,right=nums.size()-1,答案 res=-1
  • 循环 left <= right
  • 算出 mid,对比:
    • nums[mid] == target:先记录当前 mid 为候选答案,不返回!继续向右搜索,尝试找更晚出现的 target → left = mid + 1
    • nums[mid] < target:目标在右侧 → left = mid + 1
    • nums[mid] > target:目标在左侧 → right = mid – 1
  • 循环结束,res 就是右边界
  • 主函数逻辑

    分别调用获取左边界、右边界两个函数,组装成 vector 返回。

    3. 代码编写

    class Solution {
    public:
    vector<int> searchRange(vector<int>& nums, int target) {
    int leftBound = getLeftBound(nums, target);
    int rightBound = getRightBound(nums, target);
    return {leftBound, rightBound};
    }

    // 获取左边界:第一个等于target的下标
    int getLeftBound(vector<int>& nums, int target){
    int left = 0;
    int right = nums.size() – 1;
    int res = -1;
    while(left <= right){
    int mid = left + (right – left) / 2;
    if(nums[mid] == target){
    res = mid;
    right = mid – 1;
    }else if(nums[mid] < target){
    left = mid + 1;
    }else{
    right = mid – 1;
    }
    }
    return res;
    }

    // 获取右边界:最后一个等于target的下标
    int getRightBound(vector<int>& nums, int target){
    int left = 0;
    int right = nums.size() – 1;
    int res = -1;
    while(left <= right){
    int mid = left + (right – left) / 2;
    if(nums[mid] == target){
    res = mid;
    left = mid + 1;
    }else if(nums[mid] < target){
    left = mid + 1;
    }else{
    right = mid – 1;
    }
    }
    return res;
    }
    };


    三、两篇对比总结

    表格

    题目数组特点命中 target 行为作用
    704 普通二分 有序无重复 直接 return 判断是否存在,返回任意一个位置
    34 边界二分 有序可重复 记录答案,继续向一侧压缩区间 找左 / 右边界

    核心口诀:有序才能二分;普通二分找到就走,边界二分找到不松手,往一边挤!

    赞(0)
    未经允许不得转载:171主机测评 » 【算法拾光】二分查找|二分查找 + 再排序数组中查找元素的第一个和最后一个位置
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址