「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。
题目描述
给定一个按照升序排列的整数数组 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]
提示:
- 0 <= nums.length <= 105
- 109 <= nums[i] <= 109
- nums 是一个非递减数组
- 109 <= target <= 109
思路介绍
题意分析
很容易看出来是二分查找,尤其是进阶要求O(log n),就可以百分百确定是使用二分查找了。 二分查找思想并不难,难点在细节。 大于还是大于等于?向上取整还是向下?缩进区间是加一减一还是不变?最终结果到底是左边界还是右边界?
在进行二分查找时,low会增大,high会减小。如果循环条件是low<=high,则循环结束时high在low前面。要找右边的某个数时,要借助low实现,因为low会不断变大。要找左边的某个数时,要借助high实现,因为high会不断变小。要找=target的最左边的数时,也就是找满足>=target的最左边的数。但是,当nums[mid]>=target时,无法判断nums[mid]是否是满足nums[mid]>=target的最左边的数。所以不能让high = mid。但是,当nums[mid]>=target时,可以判断nums[mid]是满足nums[mid]>=target的数。所以,让high = mid - 1。这样,只要满足条件,high就变小,最终high会停在第一个不满足条件的点。所以,当nums[mid]>=target时, high = mid - 1。这样,最终high就会停在第一个不满足>=target的地方。同理,要找=target的最右边的数时,也就是找<=target的最右边的数。所以,当nums[mid]<=target时, low = mid + 1。这样,最终low就会停在第一个不满足<=target的地方。
代码
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = getLeft(nums, target);
int right = getRight(nums, target);
if(right-left>1) return new int[] {left+1,right-1};
else return new int[] {-1,-1};
}
public int getLeft(int[] nums, int target){
int low = 0;
int high = nums.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] > target) {
high = mid - 1;
} else if (nums[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return high;
}
public int getRight(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] > target) {
high = mid - 1;
} else if (nums[mid] < target) {
low = mid + 1;
} else {
low = mid + 1;
}
}
return low;
}
}
运行结果
执行结果:通过
执行用时:0 ms,
内存消耗:44.3 MB
\