1.题意
给定一个可能有重复元素的非递减数列,要求找出给定元素第一次出现和最后一次出现的位置(区间),若不存在则返回{-1, -1}。注意:
a.该数列可以为空
b.对只出现一次的情况,返回{pos, pos}
2.算法
二分
3.分析
以前做二分都是查找单个元素,现在是找区间。我们可以把找区间转化为找两个端点,分别进行一次二分查找。
3.1 使用二分查找来寻找左端点的情况
3.1.1 每轮子区间的选择
33的分析里写了很长一段来说明子区间的选择,是因为那个数列不单调。这里它不递减我们就可以视为单调,直接应用结论就好(原理在33那里分析过了)。
if(nums[mid] > target)
right = mid-1;
if(nums[mid < target)
right = mid+1;
=的情况属于对mid的判断的部分。
3.1.2 对mid的判断
做这道题最开始分析的困难点就是,当nums[mid]==target时,实际可能有三种情况:
a.mid是左端点
b.mid是右端点
c.mid是中间部分(目标元素出现3次及以上的情况)
思考的时候很容易被c情况扰乱,觉得这种情况很难处理是往左还是往右。但一旦我们确定了策略是找左右端点,c情况就完全不会对我们造成任何困扰,我们只专注于mid是否符合端点的条件(ab的条件比c好找多了)。
现在我们找的是左端点,由于这是一个非递减数列,所以左端点lans的条件是:
num[lans]>num[lans-1] && num[lans] == target
这里有一个惯例的边界情况要考虑:目标点有没有可能出现在数组的边界? 以这里为例子就是,左端点有没有可能是数组第一个元素?是有可能的,而如果是这种情况,上面就会数组越界,所以在它之前加一句特判:
!lans && num[lans] == target
但是我们每轮的操作如果到此为止,是一定会进入死循环的。因为这里我们还没有对c情况进行操作!这里我们的目标是找到左端点,而如果上面两条对mid的判断都不符合,就说明我们位于目标区间的中部,即左端点在我们的左边,所以向左挪动。
else
right = mid-1;
看起来好像没什么,但其实一开始思考的时候,如何对c情况操作我想了很久很久都没想出来。之后意识到这个问题实际就是分别二分找左右端点之后,c情况一下子就迎刃而解了。
3.2 对右区间的查找
思路和做法与左区间是完全一致的,唯一不同就是条件设置相反,注意:
1.需要特判一下右端点是数组最后一个元素的情况。33题不需要特判右边是题目特点决定的,实际上做的时候两边端点的特殊情况都要考虑。
2.如果你两次二分共用了left, right变量,记得重新初始化。
4.代码
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.size() == 0)
return {-1, -1};
int left = 0, right = nums.size()-1, mid = -1, lans = -1, rans = -1;
while(left <= right){
mid = left + (right-left)/2;
if(!mid && nums[mid]==target){
lans = mid;
break;
}
if(nums[mid]==target && nums[mid-1]<nums[mid]){
lans = mid;
break;
}
else if(nums[mid] > target)
right = mid-1;
else if(nums[mid] < target)
left = mid+1;
else
right = mid-1;
}
left = 0, right = nums.size()-1, mid = -1;
while(left <= right){
mid = left + (right-left)/2;
if(mid==nums.size()-1 && nums[mid]==target){
rans = mid;
break;
}
if(nums[mid]==target && nums[mid+1]>nums[mid]){
rans = mid;
break;
}
else if(nums[mid] > target)
right = mid-1;
else if(nums[mid] < target)
left = mid+1;
else
left = mid+1;
}
if(rans == -1 && lans == -1)
return {-1, -1};
return {lans, rans};
}
};
5.复杂度
时间复杂度O(logn)
空间复杂度O(1)