这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战
题目
34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 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] <= 109nums是一个非递减数组-109 <= target <= 109
思路
- 首先先说一下暴力解法,我们可以通过一轮遍历找到当前的元素位置,然后加两个变量去存储对应开始位置和结束位置的索引,当遍历到当前元素的时候,如果开始位置的值为-1,就赋值覆盖,否则不做处理,结束位置的值每次覆盖掉即可,这样子遍历结束后只需要判断是否存在开始位置即可;
- 然后对于这道题目的话,我们要考虑到一个前置条件,是在排序数组中查找,那么我们在这种条件下,数据量大的情况下用二分查找的效率是最高的,所以我们可以用二分查找,迅速找到这个值,找到了之后,往前往后都走到值不相等时候即可。
具体而言可以分为以下三个步骤:
首先通过二分查找,快速找到一个跟当前元素值相等的索引
-
如果找不到这个值的话,说明不存在这样子的一个元素,那么开始和结束值都返回-1即可;
-
如果找到了这个值,那么我们分别以这个索引为起点,向左和向右查找,用whlie循环去判断,如果当前值往前或者往后走的值还是等于它本身,那么就继续走下去,直到走到尽头或者值不相等为止。
实现
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange = function(nums, target) {
const index = findTargetIndex(nums, target);
if (index === -1) return [-1, -1];
let prevIndex = lastIndex = index;
// 往左边走到尽头
while (nums[prevIndex] === nums[prevIndex - 1]) {
prevIndex--;
}
// 往右边走到尽头
while (nums[lastIndex] === nums[lastIndex + 1]) {
lastIndex++;
}
return [prevIndex, lastIndex];
};
// 朴实无华的二分查找
function findTargetIndex(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
结果
总结
在遇到有序的数组中查找指定的值的时候,优先考虑二分查找,这可以算是这种题型的一种通用解题思路,当然具体实现过程可能是千变万化的,不要一昧的套用公式。
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。