Search for a Range问题
Search for a Range问题
1. 问题描述
Given an array of integers sorted in ascending order, 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].
题目翻译:
给定一个按照递增排序的整数数组,查找一个数值开始和结束的位置。算法的复杂度要求必须是O(logn)。如果找到目标就返回一个int类型的数组[start,end],如果目标没有找到,返回[-1,-1]。
2.解题思路
在一个已经排序的数组中查找一个数值,并且算法的复杂度要求是O(logn),这种类型的题目使用二分法来做的,所以看到这道题基本的思路就能确定下来。难点就是怎么确定这个数值的左右边界,并且能够满足复杂度的要求。
首先要利用二分法确定这个数组中是否包含这个数值,同时能够获得这个数值一个索引,这个索引一定包含start和end的范围内。那么就以这个索引为基础想两边扩张。扩张的时候依然使用二分查找来做。当在左边的区域中找到一个对应的数值,那就以这个位置为有边界继续向左扩张范围。同样,当在右边找到一个对应的数值,就以这个数值为左边界继续向右寻找。直到找不到对应的数值,那么这个数值的范围就能确定。
3.代码实现
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res ={-1,-1};
int len = nums.length;
if(nums==null||len==0)//判断数组是否为空
return res;
int begin =0;
int end = len-1;
int pos = binarySearch(nums,0,end,target);//判断数组中是否包含target
if(pos==-1)
return res;
begin=end= pos;
while( begin != -1){//以pos为右边界向左扩张
res[0] = begin;
if(begin>0)//这里要注意边界情况,否则发生越界
begin = binarySearch(nums,0,begin-1,target);
else
begin=-1;
}
int l1 = len -1;
while(end != -1){//以pos为左边界向右扩张
res[1] = end;
if(end<l1)//注意边界情况
end = binarySearch(nums,end+1,l1,target);
else
end =-1;
}
return res;
}
//二分查找的实现
public int binarySearch(int[] nums,int begin, int end,int target){
int mid =0;
while(begin<end){
mid = begin+(end-begin)/2;//为了防止溢出的小trick
if(target==nums[mid])
return mid;
if(target>nums[mid])
begin = mid+1;
else
end = mid-1;
}
return target==nums[begin]?begin:-1;//判断是否相等,并返回索引
}
}
上面是按照自己的思路来实现的代码严格来说当数组中的元素完全重复的时候,复杂度会变成O(n),下面的代码是参考了Discuss里面排行第一的讲解写出的代码,人家的想法确实很优秀,为了方便查看,点击这里:
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res ={-1,-1};
int len = nums.length;
if(nums==null||len==0)
return res;
int begin =0;
int end = len-1;
int mid =0;
while(begin<end){//这个循环不仅确定了start的位置也确定了数组中是否存在target
mid = begin+(end-begin)/2;
if(nums[mid]<target)
begin = mid+1;
else//else if(target<=nums[mid])
//处理的技巧在这里,这样做不仅收缩了范围,end也保存了target的位置信息
end = mid;
}
if(target==nums[begin])
res[0] = begin;
else
return res;
end = len-1;
while(begin<end){//以begin为左边界向右寻找右边界
mid = (begin+end)/2+1;//这里也有一个小trick
if(nums[mid]>target)
end = mid-1;
else
begin = mid;
}
res[1] = end;
return res;
}
}