☰
【算法拾光】二分查找|二分查找 + 再排序数组中查找元素的第一个和最后一个位置
2026/10/22 21:00:02 网站建设 项目流程

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


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

1. 题目分析

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

2. 算法原理(分步)

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

  1. 定义查找区间[left, right],左端点left = 0,右端点right = nums.size()-1
  2. 循环条件:left <= right(左闭右闭,区间有效才循环)
  3. 取中间下标mid = left + (right - left) / 2,规避left+right溢出
  4. 比较nums[mid]和target:
    • nums[mid] == target:找到目标,直接返回 mid
    • nums[mid] < target:目标在右边区间,更新left = mid + 1(mid 已经比对过,不需要再包含)
    • nums[mid] > target:目标在左边区间,更新right = mid - 1
  5. 循环结束,区间空了,说明不存在 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 保持一套逻辑,方便记忆

找左边界逻辑
  1. 初始化left=0,right=nums.size()-1,答案res=-1(默认不存在)
  2. 循环left <= right
  3. 算出 mid,对比:
    • nums[mid] == target:先记录当前 mid 为候选答案,但不返回!继续向左搜索,尝试找更早出现的 target →right = mid - 1
    • nums[mid] < target:目标在右侧 →left = mid + 1
    • nums[mid] > target:目标在左侧 →right = mid - 1
  4. 循环结束,res 就是左边界,没找到维持 - 1
找右边界逻辑
  1. 初始化left=0,right=nums.size()-1,答案res=-1
  2. 循环left <= right
  3. 算出 mid,对比:
    • nums[mid] == target:先记录当前 mid 为候选答案,不返回!继续向右搜索,尝试找更晚出现的 target →left = mid + 1
    • nums[mid] < target:目标在右侧 →left = mid + 1
    • nums[mid] > target:目标在左侧 →right = mid - 1
  4. 循环结束,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 边界二分有序可重复记录答案,继续向一侧压缩区间找左 / 右边界

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询