题目链接: 在排序数组中查找元素的第一个和最后一个位置
二分查找求解, 但是普通版本的二分查找无法求解, 普通版本的二分查找每次根据中点的值与目标值进行区间划分, 但是该题数据为非递减顺序, 所以可能出现如下情况:
nums = [1, 2, 3, 3, 3, 4, 5, 6]
target = 3
此时普通版本就不好使了, 因为你能确定目标值在哪但不能确定左右端点在哪, 所以使用寻找左端点和右端点的二分查找:
1. 寻找左端点的二分查找
区间划分规则:
1) nums[m] < target : l = m + 1;
2) nums[m] >= target : r = m;
其中r = m是关键, 不能是r = m - 1, 因为r所在的位置可能是左端点, 所以不能排除该位置.
循环条件: l < r时继续, l == r时结束循环, 不能等于因为会出现死循环, 而且l == r时就是结果出现之时, 此时只需判断该位置的值是否等于目标值即可.
求中间值公式: m = l + (r - l) / 2, 不需要+1, +1会导致死循环, 当遍历区间只剩两个元素时, 可能出现如下情况:
如果此时是这种情况: nums[m] >= target : r = m, 那就死循环了, 而不+1就没问题.
2. 寻找右端点的二分查找
区间划分规则:
1) nums[m] <= target : l = m;
2) nums[m] > target : r = m - 1;
其中l = m是关键, 不能是l = m + 1, 因为l所在的位置可能是右端点, 所以不能排除该位置.
循环条件: l < r时继续, l == r时结束循环, 不能等于因为会出现死循环, 而且l == r时就是结果出现之时, 此时只需判断该位置的值是否等于目标值即可.
求中间值公式: m = l + (r - l + 1) / 2, 一定要+1, 不+1会导致死循环, 当遍历区间只剩两个元素时, 可能出现如下情况:
如果此时是这种情况: nums[m] <= target : l = m, 那就死循环了, 而+1就没问题.
寻找左端点/右端点版本的二分查找注意事项:
1. 循环条件 while(left < right).
2. 寻找左端点时求中点公式为left + (right - left) / 2, 寻找右端点时求中点公式为left + (right - left + 1) / 2.
本体题解思路: 先使用寻找左端点版本的二分查找找出左端点, 再使用寻找右端点版本的二分查找找出右端点即可.
题解代码:
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
if(nums.empty()) return {-1, -1};
vector<int> res(2, -1);
//找左端点
int left = 0;
int right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target) left = mid + 1;
else if(nums[mid] >= target) right = mid;
}
if(nums[left] == target) res[0] = left;
//找右端点
left = 0;
right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(nums[mid] <= target) left = mid;
else if(nums[mid] > target) right = mid - 1;
}
if(nums[left] == target) res[1] = left;
return res;
}
};