一. 前言
话不都说,直接上题目(LeetCode):
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
示例2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
二.题解
按照题目要求,我们需要从一个非递减顺序排列的数组里面查找值并且时间复杂度为 O(log n)
,那我们自然想到就是二分法了。
⭐简单说说二分法的思想:我们确立一个前指针和一个后指针,分别指向数组的第一个元素和最后一个元素。然后比较两个指针包裹元素的正中间那一个与目标值的大小,进而确定下一次循环中的前指针和后指针的位置,当前指针和后指针相邻时还没有找到目标值则说明这个数组中没有目标值。
就像下面这幅图一样,比如我们目标值是 12
,那整个二分法就是下面这个歌过程。
那我们来看看这道题是否也能向这样轻松解答吧。
我们注意到这道题和上面我给的例子有两个不相同的地方:
1.这道题目中的元素排列并不是严格的递增,其中也有相同的元素
2.题目中的要求是找出目标值的起始位置和结束位置,返回一个由原数组下表组成的新的数组
那么我们根据这些新的要求按照 二分法的思想 去写这道题肯定是可以解决的。❗ 既然题目给定的目标值在所给数组中不止一个,那么肯定有一个目标值排在前面,一个目标值排在后面,因此我们遍历两次。查找目标值的起始位置时可以从 左往右遍
历数组,而查找目标值的结束位置的时候我们就 从右往左
遍历。最后找到这两个值返回一个由它俩组成的新的数组就好了。
下面看看代码吧,代码中的注释解释了这道题目中的二分法的核心思想:
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums.length == 0) 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};
}
}
❤️如果对您有一点帮助,请点个赞再走吧!