题目:
给你一个按照非递减顺序排列的整数数组 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
思路:
target在数组中有如下三种情况:
- target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}。
- target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}。
- target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
这三种情况都考虑到,说明就想的很清楚了。
采用二分法寻找target在数组里的左右边界,为了让代码清晰,分别写两个二分来寻找左边界和右边界。计算出来的左边界是不包含target的左边界,右边界同理。
代码:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// leftBorder或rightBorder没被赋值,即target在nums数组范围的左边或右边
if(leftBorder == -2 || rightBorder == -2) return {-1, -1};
// target在nums数组范围中,且数组中存在target
else if(rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
// target在nums数组范围中,但数组中不存在target
else return {-1, -1};
}
private:
// 寻找左边界,不包含target
int getLeftBorder(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
int leftBorder = -2;
while(left <= right)
{
int middle = (left + right) / 2;
if(target > nums[middle])
{
left = middle + 1;
}
else
{
right = middle - 1;
leftBorder = right;
}
}
return leftBorder;
}
// 寻找右边界,不包含target
int getRightBorder(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
int rightBorder = -2;
while(left <= right)
{
int middle = (left + right) / 2;
if(target < nums[middle])
{
right = middle - 1;
}
else
{
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
};
总结:
时间复杂度: O(logn) ,其中 n 为数组的长度。二分查找的时间复杂度为 O(logn),一共会执行两次,因此总时间复杂度为 O(logn)。
空间复杂度:O(1) 。只需要常数空间存放若干变量。
主要利用二分法查找target在数组范围中的左右边界,注意左右边界的查找条件。
因为最后left == right + 1;,所以leftBorder = right; rightBorder = left;