「这是我参与2022首次更文挑战的第10天,活动详情查看: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]
题解
常规枚举
使用两个变量left和right,当 nums[i] === target 时,将下标记录到left和right,随后更新right直到找到最后一个目标值。最后返回变量值即可;
时间复杂度:O(n)
var searchRange = function(nums, target) {
let l = nums.length;
if (l == 0) return [-1, -1];
let left = -1;
let right = -1;
for(let i = 0 ; i < l ; i++){
if(nums[i] === target){
if(left === -1){
left = i
right = i
}else{
right = i
}
}
}
return [left,right]
};
二分法
数组升序排列,查找查找目标值,可以使用二分法查找,但是题目说目标值不唯一。这里需要在二分法中处理一下。
如果目标值唯一二分法套路
- 值
- ;
- 在区间上查找
- 返回
- 当 ,在区间查找
- 当 ,在区间查找
如果目标值不唯一二分法找左侧第一个目标数
- 值
- ;
这里需要处理:
,在区间查找;这里找到目标值不确定是第一个目标值
,所以需要在继续查找- 当 ,在区间查找
- 当 ,在区间查找
如果目标值不唯一二分法找右侧第一个目标数
- 值
- ;
这里需要处理:
,在区间查找;这里找到目标值不确定是右侧第一个目标值,同时也不确定目标值的下一个元素依然时目标值
,所以需要在继续查找- 当 ,在区间查找
- 当 ,在区间查找
二分法代码
var searchRange = function(nums, target) {
let l = nums.length;
if (l == 0) return [-1, -1];
let leftVal = getLeft();
if (leftVal == -1) return [-1, -1];
let rightVal = getRight();
return [leftVal, rightVal];
// 获取左侧目标值下标
function getLeft() {
let left = 0;
let right = l - 1;
while (left < right) {
let mid = left + Math.floor((right - left) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] == target) {
right = mid;
} else if (nums[mid] > target) {
right = mid;
}
}
if (nums[left] != target) {
return -1;
}
return left;
}
// 获取右侧目标值下标
function getRight() {
let left = 0;
let right = l - 1;
while (left < right) {
let mid = left + Math.ceil((right - left) / 2);
//console.log("ds", left, right);
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] == target) {
left = mid;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
if (nums[left] != target) {
return -1;
}
return left;
}
};
上述代码中获取左侧目标值下标、获取右侧目标值下标代码冗余严重可以,但是与文字描述思路一致,便于理解,后面可以优化代码
优化后代码
var searchRange = function (nums, target) {
let l = nums.length
if (l == 0) return [-1, -1]
let leftVal = getIndex(true)
if (leftVal == -1) return [-1, -1]
let rightVal = getIndex(false)
return [leftVal, rightVal]
function getIndex(sign) {
let left = 0
let right = l - 1
let index = -1
while (left <= right) {
let mid = left + Math.floor((right - left) / 2)
if (nums[mid] > target) {
right = mid - 1
} else if (nums[mid] < target) {
left = mid + 1
} else {
index = mid
if (sign) {
right = mid - 1
} else {
left = mid + 1
}
}
}
return index
}
}