版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qilei2010/article/details/51184395
题目
给定一个包含 n 个整数的排序数组,找出给定目标值 target 的起始和结束位置。如果目标值不在数组中,则返回[-1, -1]。
给出[5, 7, 7, 8, 8, 10]和目标值target=8,返回[3, 4]
二分解题思路
对有序数组应用二分法,能快速缩小目标范围,达到O(logN)。搜索区间和搜索单个元素类似,但也有不同思路。
一是先二分查找 first 第一次出现位置,再二分查找一次查找 last 最后一次出现位置,二者合并就是区间。代码参考
二是只进行一次二分(仅为了缩小目标区间),若找到目标元素target
,就直接在当前二分范围的start
和end
之间,从当前target
位置往前往后逐项比对,找到全部相同元素。
我这里列出方法二
的代码。
class Solution {
/**
*@param A : an integer sorted array
*@param target : an integer to be inserted
*return : a list of length 2, [index1, index2]
*/
public:
vector<int> searchRange(vector<int> &A, int target) {
vector<int> range;
range.push_back(-1);
range.push_back(-1);
int start = 0, end = (int)A.size() - 1;
int mid = 0 ;
//快速排除
if (A.size() == 0 || A[0] > target || A[end] < target) {
return range;
}
//二分模板
while (start + 1 < end) {
mid = start + (end - start)/2;
if (A[mid] < target) {
start = mid;
}else if(A[mid] > target){
end = mid;
}else{
//分别往前往后循环到start end
int i = 0, j = 0;
for(i = mid - 1; i >= start; i--){
if(A[i] != target ){
break;
}
}
range[0] = i + 1;
for(j = mid + 1; j <= end; j++){
if(A[j] != target ){
break;
}
}
range[1] = j - 1;
return range;
}
}
//检查首尾
if(A[start] == target){
range[0] = start;
range[1] = start;
}
if(A[end] == target){
range[0] = end;
range[1] = end;
}
return range;
}
};
参考如图:
[5,7,7,8,8,10] target = 7
s m e <- 第一轮二分,找到target
<- t -> <- 分别往左右找7
i t j <- i!=target, i+1即区间左坐标
<- j!=target, j-1即区间右坐标
当数据少时可能看不出来优势,当数据几千几万个时,目标元素区间一般很小,这时一次二分快速定位目标元素后,再往左右找会很快(因为目标元素相对全部数据来说一般比较少,循环很快会结束)。
附:二分模板
最后再附上一个经典的二分模板,记录下来以后自己也能快速回查:
重点理解:
1. 循环结束的判断条件 start + 1 < end
[start 和 end 不相邻]
2. 循环结束后对比 start end
//求非降序集合内目标第一次出现的位置
int binarySearch(vector<int> &array, int target) {
// write your code here
int start = 0, end = (int)array.size() - 1;
int mid = 0;
while (start + 1 < end) {
mid = start + (end - start)/2;
if (array[mid] == target) {
end = mid; //常见错误->return mid; 错误范例[3,3,3,3,3]
}else if(array[mid] > target){
end = mid;
}else{
start = mid;
}
}
if (target == array[start]) {
return start;
}
if (target == array[end]) {
return end;
}
return -1;
}
如图:
[5,7,7,8,9]
s m e
s m e
s m <- start + 1 = end 达到退出条件
<- 接着先比较 start 再比较 target
若要是求最后一个元素位置呢?
很简单,只需要改动如下位置:
if (array[mid] == target) {
start = mid; //将 mid 赋值给 start
//使检索目标区域向右移
...
//先比较 end ,再比较 start,因为你是求目标的最后一个位置嘛
if (target == array[end]) {
return end;
}
if (target == array[start]) {
return start;
}
如图:
[5,7,7,8]
s m e
s m e
s e <- start + 1 = end 达到退出条件
<- 接着先比较 end 再比较 target