题目:
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
首先分析一下题目:
数组是一个升序的数组,没有说数组中的元素是单一的,那么他的返回值的情况有:
1、当 target 在数组中找到了,如果 target 在数组中不止 1 个,那么返回的是 【i,j】;
2、当 target 在数组中只有一个,返回的是 【i,i】;
3、没有找到返回 【-1,-1】.
常规想法:
直接遍历数组找到 target 出现的第一个位置,记为 i, 继续遍历数组,找下一个出现的位置,记为 j ,
当然 j 可能会变化,因为可能下一个位置还是 target,当遍历到不是 target 结束循环,i j 的位置就是我们的结果,
之后就是分情况讨论了。【如下面代码的第一种解法】
从运行情况看,这显然不是最好的做法,那么该怎么做呢?二分法
利用二分思想先找其左边界,再找其右边界即可,注意找左边界的时候,由右侧逼近;
找右边界的时候,由左侧逼近,即可。【如下面代码的第二种解法】
class Solution {
public static int[] searchRange(int[] nums, int target) {
int start = -1, end = -1;
boolean first = false; // 记录是否是第一次定位到 target
int len = nums.length;
for(int i=0;i<len;i++){
if(!first){
if(target == nums[i]){
start = i;
first = true;
}
} else {
if(target == nums[i]){
end = i;
}
}
}
int[] res = new int[] {-1, -1};
if(first){ // 找到了 target
res[0] = start;
if(end == -1){ // target 出现的次数不止一次
res[1] = start;
} else {
res[1] = end;
}
return res;
}
// 没有找到 target
return res;
}
}
class Solution {
public static int[] searchRange(int[] nums, int target) {
int[] res = new int[] {-1, -1};
res[0] = findRange(nums, target, true); //找左边界
res[1] = findRange(nums, target, false); //找右边界
return res;
}
public static int findRange(int[] nums,int target,boolean left_right){
int res = -1;
int left = 0, right = nums.length - 1, mid;
while(left <= right) { //二分查找
mid = (left + right)>>1;
if(target < nums[mid]) {
right = mid - 1;
} else if(target > nums[mid]) {
left = mid + 1;
} else {
res = mid;
if(left_right) { //往左边找
right = mid - 1;
} else { //往右边找
left = mid + 1;
}
}
}
return res;
}
}