Leetcode 34. 在排序数组中查找元素的第一个和最后一个位置
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情。
1、题目📑
给定一个按照升序排列的整数数组 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 <= 10E5-10E9 <= nums[i] <= 10E9nums是一个非递减数组-10E9 <= target <= 10E9
进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?
2、思路🧠
方法一:二分查找
本题的难点在于在看到题目下面的进阶版本的时候就应该要想到使用二分法进行求解,但是具体的过程,还是需要进一步分析之后才能进行求解。
先复习一下二分查找:
public int search(int[] nums, int target) {
int l = 0, r = nums.length - 1, mid = 0;
while (l <= r) {
mid = l + ((r - l) >> 1);
if (nums[mid] == target) {
return mid;
}
if (nums[mid] < target) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return -1;
}
第一次查找:
- 初始化:
- l = 0, r = nums.size() - 1,寻找边界条件
- 当
nums[mid] >= target时,将r = mid进行左半区查找。 - 当
nums[mid] < target时,将l = mid + 1进行右半区查找。 - 如果
nums[R] != target,表示其数组中根本不存在所谓的目标值target,直接返回结果。
第二次查找:
- 初始化:
- 这里需要将上一次找到的位置通过变量
L进行记录,l = 0, r = nums.size() - 1,寻找边界条件
- 这里需要将上一次找到的位置通过变量
- 当
nums[mid] <= target时,将l = mid进行右半区查找。 - 当
nums[mid] < target时,将r = mid - 1进行左半区查找。 - 找到
最后一个<=target的位置R,返回区间即可。
注意:由于有序的一边的边界值可能等于目标值,所以在判断目标值是否在有序一侧时应该加上等号,换句话说也就是在二分查找某个具体值的时候如果边界值不好把握,可以在每次查找前判断边界值,也就是说while循环里面的两个if注释。
while(left <= right){
// if(nums[left] == target) return left;
// if(nums[right] == target) return right;
int mid = left + ((right - left) >> 1);
}
废话少说~~~~~上代码!
3、代码👨💻
第一次commit AC
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums.length < 1) return new int []{-1, -1};
int l = 0, r = nums.length - 1;
while(l < r) {
int mid = (l + r) / 2;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
if(nums[r] != target) return new int []{-1, -1};
int L = r;
l = 0;
r = nums.length - 1;
while(l < r) {
int mid = (l + r + 1) / 2;
if(nums[mid] <= target) l = mid;
else r = mid - 1;
}
return new int []{L,r};
}
}
时间复杂度:O(log N)
空间复杂度:O(1)
4、总结
该题目的对二分法的过程以及思想通过对题目的灵活变换的考察,如果能够理解二分过程十分透彻的话,拿下此题应该不是问题。
二分模板1:
while(l < r) {
int mid = (l + r + 1) / 2;
if(nums[mid] <= target) l = mid;
else r = mid - 1;
}
二分模板2:
while(l < r) {
int mid = (l + r) / 2;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
初始时我们的二分区间为[l,r],每次二分缩小区间时,对于模板1,如果选择左边界为 l = mid,此时 mid 的取值就应为 mid = (l + r + 1)/ 2 ,这里需要注意当右边界执行最后一次为 r == l 时,此时 mid = (l + l + 1)/ 2 ,这里向上取整,mid依然等于l 这里就掉入了死循环。
❤️来自专栏《LeetCode基础算法题》欢迎订阅❤️
厂长写博客目的初衷很简单,希望大家在学习的过程中少走弯路,多学一些东西,对自己有帮助的留下你的赞赞👍或者关注➕都是对我最大的支持,你的关注和点赞给厂长每天更文的动力。
对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!