问题描述:
给定一个按照升序排列的整数数组 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]
解题思路:
看到有序、整数数组,时间复杂度为log(n),就可以基本确定这个题可以用二分法来解决了。
二分法最重要的就是确定左右边界。
针对本题,寻找target在数组里的左右边界,有如下三种情况:
- 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
- 情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
- 情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
这三种情况都考虑到,说明就想的很清楚了。
针对二分解法,通常有两种解题思路:
1. 左右区间为左闭右闭([a,b])
2. 左右区间为左闭右开([a,b))
本题推荐采用左闭右闭的方式来解题。
解题思路为:采用二分法分别求得左边界和右边界
求左边界时,从右向左逼近,从而保证求得的边界为最左。
同理求右边界时,从左向右逼近,从而保证求得的边界为最右。
需要注意的一点是,采用左闭右闭求解到的左边界是左边界左边的元素,因此最后的结果要+1.
采用左闭右闭求解到的右边界为右边界右边的元素,因此最后的结果要-1。
很多时候我们还会分不清楚求解左边界时什么时候用>=,什么时候用>。
其实这也是没有理解二分求解这道题的思想的表现。
在采用左闭右闭求解左边界时,是从右往左逼近的,而且求出的左边界其实是左边界-1;
因此当
nums[mid] = target时,
leftIndex = mid - 1;
而nums[mid] > target时,
right = mid - 1;
因此这两行代码就可以进行合并;
同理于右边界,这样就理清楚什么时候用>=,什么是>
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange = function(nums, target) {
const getLeftBorder = (nums, target) => {
let left = 0;
let right = nums.length - 1;
let leftIndex = -2;
// 利用二分思想先找其左边界,再找其右边界即可,注意找左边界的时候,由右侧逼近;找右边界的时候,由左侧逼近,即可
while (left <= right) {
let mid = left + Math.floor((right - left) / 2);
if (nums[mid] >= target) {
right = mid - 1;
leftIndex = right;
} else {
left = mid + 1;
}
}
return leftIndex;
};
const getRightBorder = (nums, target) => {
let left = 0;
let right = nums.length - 1;
let rightIndex = -2;
while (left <= right) {
let mid = left + Math.floor((right - left) / 2);
if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
rightIndex = left;
}
}
return rightIndex;
};
let leftBorder = getLeftBorder(nums, target); //在这里把值传进去
let rightBorder = getRightBorder(nums, target);
if (leftBorder === -2 || rightBorder === -2) {
return [-1, -1];
}
if (rightBorder - leftBorder > 1) return [leftBorder + 1, rightBorder - 1];
return [-1, -1];
};
console.log(searchRange([5, 7, 7, 8, 8, 10], 8));