LeetCode从低效到高效,点击
一、题目描述:
题目要求
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
来源:力扣(LeetCode)链接
示例
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
二、思路分析:
本题是一道经典的二分查找题目,是普通二分查找的变形,搜索指定元素的边界,搜索有没有元素就是简单的二分查找,查找搜算范围我最开始想到的思路就是找到指定元素,然后从左找到一个不是这个元素的位置,再去找右边,但是如果这样去搜所的话如果被查找元素的数量很多,那这个算法效率就差不多是,所以即使找到了指定元素也应该继续使用二分的策略。 搜索左边界时,如果发现元素,右指针要压过来,使搜索范围尽可能向左找,不用担心最后左指针找不到目标元素,因为这个搜索没有异常退出,所以左指针是必定大于右指针的
三、AC 代码:
先搜左边界,再搜右边界 这个版本更好理解
// 8 ms 13.4 MB
vector<int> searchRange_v1(const vector<int>& nums, int target) {
int l=0,r=nums.size()-1;
int mid;
while(l<=r){
mid = (l+r)>>1;
// 搜索左边界就是找到目标点时把右指针指过去,因为这个结构没有return出口,所以left一定会大于right指向tar,除非不存在tar
if(nums[mid]==target){
r = mid -1;
}else if (nums[mid]>target)
{
r = mid -1;
}else{
l = mid + 1;
}
}
// 注意这个顺序不能搞错,要先判断位置对不对,然后才能检查这个位置存的对不对。如果找不到左边界,那也不用搜索右边界
if(l>=nums.size()||nums[l]!=target)
return {-1,-1};
int tarleft = l;
// 搜索右端点
l=0,r=nums.size()-1;
while(l<=r){
mid = (l+r)>>1;
if(nums[mid]==target){
l = mid + 1;
}else if (nums[mid]>target)
{
r = mid - 1;
}else{
l = mid + 1;
}
}
return {tarleft,r};
}
压缩判断条件
// 8 ms 13.1 MB
vector<int> searchRange(const vector<int>& nums, int target) {
int l=0,r=nums.size()-1;
int mid;
while(l<=r){
mid = (l+r)>>1;
if(nums[mid]<target){
l = mid + 1;
}else{
r = mid -1;
}
}
if(l>=nums.size()||nums[l]!=target)
return {-1,-1};
int tarleft = l;
l=0,r=nums.size()-1;
while(l<=r){
mid = (l+r)>>1;
if (nums[mid]>target)
{
r = mid - 1;
}else{
l = mid + 1;
}
}
return {tarleft,r};
}
四、总结:
搜索元素范围是一道二分查找的魔改题目,程序的主题框架并没有改变,我更习惯二分查找使用左闭右闭的搜索范围,如果习惯左闭右开也都可以
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情