给定一个按照升序排列的整数数组 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 <= 10^5
-10^9 <= nums[i] <= 10^9
nums 是一个非递减数组
-10^9 <= target <= 10^9
看到这道题,可以看出时间复杂度为 O ( n ) O(n) O(n)的方法是很好搞定的,然而,如果想要优化到 O ( l o g ( n ) ) O(log(n)) O(log(n))则需要利用二分查找的方法,用三个指针, l , r , m i d l,r,mid l,r,mid形成分块查找,然而,这道题特殊的地方在于他可能有很多个待查找的值,因此,我也不知道最开始想出来的办法算不算 O ( l o g ( n ) ) O(log(n)) O(log(n))的方法,具体我们直接看代码吧。
方案一(上文提到的的方法):
首先,我们做一个fail数组,用于没有找到目标值的情况,也就是loc=-1,其次,我们只要找到了一个待查找值就退出循环,这个时间复杂度是 O ( l o g ( n ) ) O(log(n)) O(log(n))的。然而,我们后面还要有一步是查找整个串的头尾所在位置,假设有k个值为target的元素,那么时间复杂度就是 O ( k ) O(k) O(k)因此整个的时间复杂度为 O ( l o g ( n ) + k ) O(log(n)+k) O(log(n)+k)。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int l=0;
int r=nums.size()-1;
int loc=-1;
int mid;
vector<int> fail;
vector<int> success;
fail.push_back(-1);
fail.push_back(-1);
while ( l<=r ) {
mid = l + (r-l)/2;
if ( nums[mid] < target ) {
l = mid+1;
} else if ( nums[mid] > target ) {
r = mid-1;
} else {
loc = mid;
break;
}
}
if ( loc == -1 ) return fail;
int templ=loc;
int tempr=loc;
while ( templ >= 0 && nums[templ] == target ) templ--;
templ++;
while ( tempr < nums.size() && nums[tempr] == target ) tempr++;
tempr--;
success.push_back(templ);
success.push_back(tempr);
return success;
}
};
方案2:
看了官方题解后感觉如梦初醒,原来可以直接找左右边界,学到了…不过今天时间有点紧了,之前看到官方题解写的虽然简练,但是丧失了一些代码的易读性,因此我自己写的易读版如下。
class Solution {
public:
int find_left(vector<int> nums, int target) {
int l=0;
int r=nums.size()-1;
int res=-1;
int mid;
while ( l<=r ) {
mid = l + (r-l)/2;
if ( nums[mid] >= target ) {
r = mid-1;
res = mid;
} else {
l = mid+1;
}
}
return res;
}
int find_right(vector<int> nums, int target) {
int l=0;
int r=nums.size()-1;
int res=-1;
int mid;
while ( l<=r ) {
mid = l + (r-l)/2;
if ( nums[mid] > target ) {
r = mid-1;
} else {
l = mid+1;
res = mid;
}
}
if ( res!=-1 && nums[res]!=target ) return -1;
return res;
}
vector<int> searchRange(vector<int>& nums, int target) {
int l = find_left(nums, target);
int r = find_right(nums, target);
if ( l>r ) return vector<int>{-1,-1};
return vector<int>{l,r};
}
};
二分并不难,难的是细节,继续努力!