给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
题目链接:中文题目;英文题目
使用二分法有两种思路:1)二分不断查找target的最小序号和最大序号;2)二分查找一个target的序号,然后左右两边分别查找第一个小于target数的序号,和第一个大于target数的序号。
因为第一种方法比较易于理解,所以这里着重解释一下第一种方法。至于第二种方法,大家可以根据下面的代码,自己理解一下。
我们以下面这个为例子来讲解:
[5,7,7,8,8,10], target = 8
番外:
(1)upper_bound函数是在[first, last)中查找第一个比target大的数,找不到返回last;
(2)lower_bound函数实在[first, last)中查找第一个大于或等于target的数,找不到则返回last;
(3)两者的返回值都是迭代器(Iterator);
class Solution {
int binarySearch(vector<int>& nums, int left, int right, int target) {
if (left == right) { if (nums[left] == target) return left; else return -1; }
else if (left > right) return -1;
int mid = (right - left) / 2 + left;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) return binarySearch(nums, mid + 1, right, target);
return binarySearch(nums, left, mid - 1, target);
}
int findLowerBound(vector<int>& nums, int left, int right, int target) {
if (left == right) return left;
else if (left > right) return -1;
if (nums[left] == target && nums[right] == target) return left; // 查找的数组的数字都是target
int mid = (right - left) / 2 + left;
if (nums[mid] == target) return findLowerBound(nums, left, mid, target);
if (nums[mid] < nums[mid + 1] && nums[mid + 1] == target) return mid + 1;
return findLowerBound(nums, mid + 1, right, target);
}
int findUpperBound(vector<int>& nums, int left, int right, int target) {
// 上界会出现这种特殊情况:[2,2] 或 [2,3],都是查找2
if (left == right) {
if (nums[left] == target) return left;
else return left - 1;
}
else if (left > right) return -1;
if (nums[left] == target && nums[right] == target) return right;
int mid = (right - left) / 2 + left;
if (nums[mid] == target) return findUpperBound(nums, mid + 1, right, target);
if (nums[mid - 1] < nums[mid] && nums[mid - 1] == target) return mid - 1;
return findUpperBound(nums, left, mid - 1, target);
}
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> ans(2, -1);
int len = nums.size(), idx = binarySearch(nums, 0, len - 1, target);
if (idx == -1) return ans;
//(1)使用自己创建的二分查找函数查找第一个比target小的数,和第一个比target大的数
//ans[0] = findLowerBound(nums, 0, idx, target);
//ans[1] = findUpperBound(nums, idx, len - 1, target);
//(2)使用lower_bound和upper_bound内置函数进行查找,思路和(1)相似
//vector::iterator itlower = lower_bound(nums.begin(), nums.begin() + idx, target);
//ans[0] = itlower - nums.begin();
//vector::iterator itupper = upper_bound(nums.begin() + idx + 1, nums.end(), target);
//ans[1] = itupper == nums.end() ? (idx + 1 <= len - 1 ? len - 1 : idx) : itupper - nums.begin() - 1;
//(3)直接使用二分查找函数查找target在数组中最大的序号和最小的序号
int idxLowerLeft = 0, idxLowerRight = idx - 1, idxUpperLeft = idx + 1, idxUpperRight = len - 1;
ans = { idx, idx };
while (idxLowerLeft <= idxLowerRight) {
int idxFound = binarySearch(nums, idxLowerLeft, idxLowerRight, target);
if (idxFound == -1) break;
ans[0] = min(ans[0], idxFound);
idxLowerRight = idxFound - 1;
}
while (idxUpperLeft <= idxUpperRight) {
int idxFound = binarySearch(nums, idxUpperLeft, idxUpperRight, target);
if (idxFound == -1) break;
ans[1] = max(ans[1], idxFound);
idxUpperLeft = idxFound + 1;
}
return ans;
}
};