大家好!我是曾续缘💯
今天是《LeetCode 热题 100》系列
发车第 65 天
二分查找第 3 题
❤️点赞 👍 收藏 ⭐再看,养成习惯
给你一个按照非递减顺序排列的整数数组
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
解题方法
题目要求我们在一个已经排序的数组中找到目标值 target
的第一个和最后一个位置。
首先,我们定义一个二分查找的函数 bs
,返回大于等于目标值的第一个数组位置。使用的是左闭右闭的写法,需要讲究的是当nums[mid] = target
时,缩小的是r
指针,往等于目标值的第一个数组位置的方向上靠。
这样我们使用bs(nums, target)
就得到了大于等于目标值的第一个数组位置l
。
- 注意这个
l
位置可能是大于目标值的,如果是大于的话,此时最后一个位置也是大于目标值的,因此返回[-1, -1]
。
接着我们找大于等于目标值的最后一个位置。
由于int
类型是离散的,我们找大于等于目标值+1
的数的第一个数组位置,它的前一个位置就是目标值的最后一个位置了。
因此,使用bs(nums, target + 1)
得到大于等于目标值+1
的数的第一个数组位置r
,目标值的最后一个位置r-1
。
返回答案[l, r - 1]
。
Code
查看代码
java
class Solution {
public int[] searchRange(int[] nums, int target) {
int l = bs(nums, target);
if(l < nums.length && nums[l] == target){
int r = bs(nums, target + 1);
return new int[]{l, r - 1};
}
return new int[]{-1, -1};
}
public int bs(int[] nums, int target) {
int n = nums.length;
int l = 0, r = n - 1;
while(l <= r){
int mid = (l + r) / 2;
if(nums[mid] >= target){
r = mid - 1;
}else{
l = mid + 1;
}
}
return l;
}
}
另一种写法:
bs_l
找到目标值时,往第一个靠,当靠出界即l>r
后,返回l
。
bs_r
找到目标值时,往最后一个靠,当靠出界即l>r
后,返回r
。
查看代码
java
class Solution {
public int[] searchRange(int[] nums, int target) {
int l = bs_l(nums, target);
if(l < nums.length && nums[l] == target){
int r = bs_r(nums, target);
return new int[]{l, r};
}
return new int[]{-1, -1};
}
public int bs_l(int[] nums, int target) {
int n = nums.length;
int l = 0, r = n - 1;
while(l <= r){
int mid = (l + r) / 2;
if(nums[mid] >= target){
r = mid - 1;
}else{
l = mid + 1;
}
}
return l;
}
public int bs_r(int[] nums, int target) {
int n = nums.length;
int l = 0, r = n - 1;
while(l <= r){
int mid = (l + r) / 2;
if(nums[mid] > target){
r = mid - 1;
}else{
l = mid + 1;
}
}
return r;
}
}