「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。
题目:给定一个升序数组和一个目标值,要求找出该目标值在数组的开始和结束位置,如果目标值不存在则返回[-1, -1]。要求时间复杂度为。
解题思路
有序数组且时间复杂度为,则首先想到二分法,暂且不看时间复杂度。因为数组有序,我们可以将数组分别从前往后和从后往前进行扫描,如果左边数组的元素等于目标值则记录,这样能确保元素是从前往后数第一个,右边同理可保证元素从后往前数是第一个,这样也能得到答案,代码如下:
public static int[] searchRange(int[] nums, int target) {
int left=-1, right=-1;
int l=0, r=nums.length-1;
while(l<=r){
if(nums[l]==target){
left = l;
}else {
l++;
}
if(nums[r]==target){
right = r;
}else {
r--;
}
if(left!=-1&&right!=-1){
break;
}
}
return new int[]{left, right};
}
实际运行时间1ms,上述代码时间复杂度为, 空间复杂度为。思想类似双指针,那么如何用二分法在此处呢?直接使用二分可能会遇到nums[mid]==target且nums[mid-1]=nums[mid+1]=target的情况,这种情况会比较复杂,因此此处可以对问题进行转化。
转化思路为:
- 题目所需是获取指定元素的开始和结束索引,那么可以转化成寻找一个数组中大于某个元素的第一个索引。
- 即寻找大于
target-1的第一个索引,若存在则是target的第一个索引。 - 寻找大于
target的第一个索引,若存在则index-1即为target的最后一个索引。
此处的index的初始化必须为数组的长度,因为如果数组中不存在大于target的元素,则代表当前target必然是数组的最后一个元素,而之后需要将得到的index-1,则此时应该返回数组的长度。代码如下:
public static int[] searchRange2(int[] nums, int target) {
int left = binarySearch(nums, target-1);
int right = binarySearch(nums, target) - 1;
if(left<=right){
return new int[]{left, right};
}
return new int[]{-1, -1};
}
public static int binarySearch(int[] nums, int target){
int l=0, n=nums.length, r=n-1, index=n; //此处index初始化必须为数组长度,否则报错
while (l<=r){
int mid = (l + r) / 2;
if(nums[mid]>target){
r=mid-1;
index=mid;
}else {
l=mid+1;
}
}
return index;
}
上述算法因为是二分查找,其算法的时间复杂度为,算法中用了常数项的变量,空间复杂度为。