Given a sorted array of integers, find the starting and ending position of a given target value.
Your algorithm’s runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1]
.
For example,
Given [5, 7, 7, 8, 8, 10]
and target value 8,
return [3, 4]
.
查找某一个值出现在数组中的范围。
方案一:
先二分查找到key,然后再二分查找,知道arr[mid]==key && arr[mid-1] < key时为最左边界,arr[mid]==key && arr[mid-1] > key为最右边界。但是要注意考察mid==0 和 mid==n-1时的情况。所以判断时还要加强条件。代码如下
int SearchRange(int arr[], int n, int key,int *left,int *right)
{
int lo = 0, hi = n - 1, mid = 0;
int lhi, rlo;
while (lo <= hi){
mid = (lo + hi) / 2;
if (arr[mid] == key){
lhi = mid; //要取等于mid,防止左半区不再含有key值
rlo = mid; //同上,防止右半区不含key值
while (lo <= lhi){
mid = (lo + lhi) / 2;
if (arr[mid] == key && (mid == 0 || arr[mid - 1] < key)){
*left = mid;
break;
}
if (arr[mid] >= key) //arr[mid] == key的情况要放入这个里面
lhi = mid - 1;
else
lo = mid + 1;
}
while (rlo <= hi){
mid = (rlo + hi) / 2;
if (arr[mid] == key && (mid == n - 1 || arr[mid + 1] > key)){
*right = mid;
break;
}
if (arr[mid] > key)
hi = mid - 1;
else //arr[mid] == key的情况要放入这个里面
rlo = mid + 1;
}
return 1;
}
if (arr[mid] > key)
hi = mid - 1;
else
lo = mid + 1;
}
return 0;
}
方案二:
上面写的有些繁琐,还要先查到再左右加强条件遍历。我们可以分两次二分查找,第一次查找最左端,当arr[mid] >= key时,依然让hi = mid-1。这样得到的越界l刚好为左边界left;同理,第二次查找右边界,当arr[mid] <= key时,依然让l = mid+1。越界的r刚好为右边界。(这两个地方需要仔细考虑好,不然又会像上面一样,在中间添加很多代码来判断)
上面的思路充分利用了循环l<=r的特性。合适超出边界。还有判断时,等号应处于哪个范围。
当然最后还要判断查找失败时的情况。
class Solution {
//Binary Search
//we must know how to process the 3 cases below:
//1.how to find the right most target
//2.how to find the left most target
//3.how to find the insert position
public:
vector<int> searchRange(int A[], int n, int target) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int l = 0;
int r = n-1;
while (l <= r)
{
int mid = l+(r-l)/2;
if (A[mid] >= target)//find the left most target
r = mid-1;
else if (A[mid] < target)
l = mid+1;
}
int left = l;
l = 0;
r = n-1;
while (l <= r)
{
int mid = l+(r-l)/2;
if (A[mid] > target)//find the right most target
r = mid-1;
else if (A[mid] <= target)
l = mid+1;
}
int right = r;
if(A[left] != target || A[right] != target)
left = right = -1;
vector<int> ans(2);
ans[0] = left;
ans[1] = right;
return ans;
}
};
还有一种是传递一个标志符,开表示查找左边界还是右边界。简化了代码。但要考虑清楚如何判断
class Solution {
public:
int findPos(int a[], int beg, int end, int key, bool findLeft)
{
if (beg > end)
return -1;
int mid = (beg + end) / 2;
if (a[mid] == key)
{
int pos = findLeft ? findPos(a, beg, mid - 1, key, findLeft) : findPos(a, mid + 1, end, key, findLeft);
return pos == -1 ? mid : pos;
}
else if (a[mid] < key)
return findPos(a, mid + 1, end, key, findLeft);
else
return findPos(a, beg, mid - 1, key, findLeft);
}
vector<int> searchRange(int A[], int n, int target) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int leftPos = findPos(A, 0, n - 1, target, true);
int rightPos = findPos(A, 0, n - 1, target, false);
vector<int> ret;
ret.push_back(leftPos);
ret.push_back(rightPos);
return ret;
}
};