题目链接:**. - 力扣(LeetCode)**
题目描述:
给你一个按照非递减顺序排列的整数数组
nums
,和一个目标值target
。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值
target
,返回[-1, -1]
。你必须设计并实现时间复杂度为
O(log n)
的算法解决此问题。示例 1:
输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4]示例 2:
输入:nums = [5,7,7,8,8,10], target = 6 输出:[-1,-1]示例 3:
输入:nums = [], target = 0 输出:[-1,-1]提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums
是一个非递减数组
-109 <= target <= 109
算法思路:
❍ 用的还是二分思想,就是根据数据的性质,在某种判断条件下将区间一分为二,然后舍去其中一个区间,然后在另一个区间内查找;(二段性)
❍ 方便叙述,用 x
表示该元素,resLeft
表示左边界,resRight
表示右边界
寻找左边界思路:
✸ 寻找左边界:
我们注意到以左边界划分的两个区间的特点:
1️⃣ 左边区间[left,resLeft - 1]
都是小于x的;
2️⃣ 右边区间(包括左边界)[resleft,right]
都是大于等于x的
✸ 因此,关于mid
的落点,我们可以分为下面两种情况
◦ 当我们的mid落在[left,resLeft - 1]
区间的时候,也就是arr[mid] < target
。说明 [left, mid]
都是可以舍去的,此时更新 left
到 mid + 1
的位置,继续在 [mid + 1, right]
上寻找左边界;
◦ 当 mid
落在 [resLeft, right]
的区间的时候,也就是 arr[mid] >= target
。说明 [mid + 1, right]
(因为 mid 可能是最终结果,不能舍去)是可以舍去的,此时更新 right 到 mid 的位置,继续在 [left, mid]
上寻找左边界;
✸ 由此,就可以通过二分,来快速寻找左边界;
注意:这里找中间元素需要向下取整。
因为后续移动左右指针的时候:
❍ 左指针: `left = mid + 1`,是会向后移动的,因此区间是会缩小的;
❍ 右指针: right = mid
,可能会原地踏步(比如:如果向上取整的话,如果剩下 1,2 两个元素, left == 1
,right == 2
, mid == 2
。更新区间之后, left,right,mid
的值没有改变,就会陷入死循环)。
因此一定要注意,当 right = mid
的时候,要向下取整。
寻找右边界思路:
• 寻右左边界:
◦用 resRight
表示右边界;
◦ 我们注意到右边界的特点:
▪ 左边区间 (包括右边界) [left, resRight]
都是小于等于 x 的;
▪ 右边区间 [resRight+ 1, right]
都是大于 x 的;
• 因此,关于 mid 的落点,我们可以分为下面两种情况:
◦ 当我们的 mid 落在 [left, resRight]
区间的时候,说明 [left, mid - 1]
( mid 不可以舍去,因为有可能是最终结果) 都是可以舍去的,此时更新 left
到 mid
的位置;
◦ 当 mid 落在 [resRight+ 1, right] 的区间的时候,说明 [mid, right] 内的元素是可以舍去的,此时更新 right 到 mid - 1 的位置;
• 由此,就可以通过二分,来快速寻找右边界;
注意:这里找中间元素需要向上取整。
因为后续移动左右指针的时候:
• 左指针: left = mid
,可能会原地踏步(比如:如果向下取整的话,如果剩下 1,2 两个元素, left == 1, right == 2,mid == 1
。更新区间之后, left,right,mid
的值
没有改变,就会陷入死循环)。
• 右指针: right = mid - 1,是会向前移动的,因此一定要注意,当 right = mid
的时候,要向下取整。因此区间是会缩小的;
二分查找算法总结:
请大家一定不要觉得背下模板就能解决所有二分问题。二分问题最重要的就是要分析题意,然后确定
要搜索的区间,根据分析问题来写出二分查找算法的代码。
要分析题意,确定搜索区间,不要死记模板,不要看左闭右开什么乱七八糟的题解
要分析题意,确定搜索区间,不要死记模板,不要看左闭右开什么乱七八糟的题解
要分析题意,确定搜索区间,不要死记模板,不要看左闭右开什么乱七八糟的题解
重要的事情说三遍。
模板记忆技巧:
❍ 关于什么时候用三段式,还是二段式中的某一个,一定不要强行去用,而是通过具体的问题分析情况,根据查找区间的变化确定指针的转移过程,从而选择一个模版。
❍ 当选择两段式的模版时:
◦ 在求mid
的时候,只有right - 1
的情况下,才会向上取整(也就是 +1
取中间数)
算法代码:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
// 数组为空
if(nums.size() == 0) return {-1,-1};
// 二分左断点
int begin = -1,end = -1;
int left = 0,right = nums.size()-1;
while(left < right){
int mid = left + (right - left)/2;
if(nums[mid] < target)
left = mid + 1;
else
right = mid;
}
if(nums[left] != target) return{-1,-1};
else begin = left;
// 二分右端点
left = 0,right = nums.size()-1;
while(left < right){
int mid = left + (right - left + 1)/2;
if(nums[mid] <= target)
left = mid;
else
right = mid - 1;
}
return {begin , right};
}
};