Offer 驾到,掘友接招!我正在参与 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 <= 10^5
-10^9 <= nums[i] <= 10^9
nums 是一个非递减数组
-10^9 <= target <= 10^9
二、题目和思路分析:
这这这,和上一题有什么区别吗?
为什么会有这种奇奇怪怪的题目啊!!!
我直接一个return [nums.indexOf(target), nums.lastIndexOf(target)]解决问题啊啊啊!
可是我想了想,这道题大概也是要考察时间复杂度,于是我大手一挥,把双指针的代码改了一下。
不过改的过程中遇到了很多问题,比如各种情况的考虑不周到,毕竟查找两个下标比查找一个下标复杂了一倍,最后写了半天总算通过了。
可是这用时,这内存,不禁让我怀疑自己的思路是否正确。
那么是否有更简单的方法呢?
我想了又想,那大概是二分法了。
因为数组是有序的,二分法可以快速的定位到目标值事都存在于当前的二分数组中,直接循环遍历判断即可。
三、代码:
代码实现如下:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange = function(nums, target) {
let left = 0
let right = nums.length-1
let arr = [-1, -1]
while (left <= right) {
let mid = left + right >> 1
if (nums[mid] == target) {
arr[0] = arr[1] = mid
while(arr[0] > left && nums[arr[0] - 1] == target){
arr[0]--
}
while(arr[1] < right && nums[arr[1] + 1] == target){
arr[1]++
}
break
}else if (nums[mid] < target){ // 继续往右找
left = mid + 1
}else{ // 继续往左找
right = mid - 1
}
}
return arr
};
四、总结:
做了上一道题,我再看这道题就觉得更加简单了,想着这道题其实应该被归到简单类型里面。实际上我在这道题上使用双指针的解法时浪费了很多时间,主要是细节没把握好。双指针加双下标确实比较乱,容易有想不到的地方。
改成二分法之后,整体逻辑变得更加明了简单,速度也提升了近5倍,早早放弃双指针遍历才对。
加油吧!