题目
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
题解
题目要求时间复杂度为O(log n)来解决此问题,所以首先排除了暴力解法,因为暴力解法的话,时间复杂度要到达O(n)
那么这道题就可以思考使用二分查找进行解决,为什么这么想呢?
大家都知道二分查找可以很快地从一个数组中,找到目标值。
可能不知道的是二分查找还能找目标值得边界。
怎么找呢?如下代码。
public int rightBorder (int[] nums, int target){
int left = 0, right = nums.length - 1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if (nums[mid] <= target){
left = mid + 1;
}else {
right = mid - 1;
}
}
return left;
}
这段代码就可以从一个数组中找到目标值得右边界。
大家可以思考一下,当 if (nums[mid] <= target)
时 也就是左边界小于或等于目标值的话就一直往右移,那么当循环结束的时候,原先的左界就成了右边界
同理,可以找到左边界
public int leftBorder (int[] nums, int target){
int left = 0, right = nums.length - 1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if (nums[mid] < target){
left = mid + 1;
}else {
right = mid - 1;
}
}
return right;
}
一级标题
整体代码
public int[] searchRange(int[] nums, int target) {
int leftBor = leftBorder(nums, target);
int rightBor = rightBorder(nums, target);
if (leftBor > nums.length - 1 || rightBor < 0){
return new int[]{-1,-1};
}
if (rightBor - leftBor < 2){
return new int[]{-1,-1};
}else {
return new int[]{leftBor + 1, rightBor - 1};
}
}
public int leftBorder (int[] nums, int target){
int left = 0, right = nums.length - 1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if (nums[mid] < target){
left = mid + 1;
}else {
right = mid - 1;
}
}
return right;
}
public int rightBorder (int[] nums, int target){
int left = 0, right = nums.length - 1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if (nums[mid] <= target){
left = mid + 1;
}else {
right = mid - 1;
}
}
return left;
}
当我们找到左边界和右边界后,可以很容易得到答案 return new int[]{leftBor + 1, rightBor - 1};
但这里要警惕两种特殊情况,数组中没有目标值(这里分为两种情况,即目标值在数组包含范围内,或目标值在数组包含范围外)
数组中没有目标值(目标值在数组包含范围内)
这时右边界 - 左边界 应该是 < 2 的,因为就算只有一个目标值,那也是 = 2,所以
if (rightBor - leftBor < 2){
return new int[]{-1,-1};
}
数组中没有目标值(目标值在数组包含范围外)
这时一个边界会不动,一个边界会一直移动直至不能满足循环条件跳出,所以
if (leftBor > nums.length - 1 || rightBor < 0){
return new int[]{-1,-1};
}