在排序数组中查找元素的第一个和最后一个位置
分析
看到排序数组,我们就可以用暴力法,直接从前向后遍历,但是这样的话,时间复杂度O(N),就不满足题中的O(logN)。
对于O(logN)的题目,我们就可以考虑二分法来解决这种问题。
然后我们把问题进行拆分,就可以看做是两个子问题。
- 第一个就是找左边第一次出现的目标值位置,找不到就返回-1
- 第二个就是找右边第一次出现目标值的位置,找不到就返回-1,注意这里左右两边返回的位置是可以相同的。
在此基础上,我们可以做一个优化,就是当左边第一次查找返回的目标位置为-1,也就是数组中没有该目标元素,我们就不需要第二次进行查找了,因为数组中就没有该元素,直接返回一个{-1,-1}
的数组。
常见的二分法是这样的
左闭右闭区间 [left,right]
while(left <= right)
{
int mid = (left + right)>>1;//找到中间节点的位置
if(nums[mid] == target) return mid;
else if(nums[mid] > target) right = mid - 1;//要找的元素在中间位置的左边,移动右边区间
else if(nums[mid] < target) left = mid + 1;//要找的元素在中间位置的右边,移动左边区间
}
return -1;//没有找到返回-1
- 为什么在没有找到的时候,转移方程是
right = mid-1 left = mid + 1
呢?
首先,我们的区间时左闭右闭的一个闭区间,也就是说我们的left 和 right
两个下标位置我们要确定是有效的位置,这两个位置一定是没有查找的。
在每一次比较的时候,我们都是查找了mid
这个中间位置 (这是一个有序数组)
nums[mid] > target
:这种情况下,也就是我们需要查找的目标数据在mid
位置的左边,并且mid
位置我们已经找过了,但是没有结束,说明了当前mid
位置不是目标数据,我们就需要移动right
来缩小下一次我们查找时的区间大小,就得把right
变成mid的前一个位置
mid - 1nums[mid] < target
:这种情况也就是我们需要查找的数据在mid
位置的右边,我们就需要移动left
的值来缩小下一次查找时区间的范围,所以说left
就变成了mid的下一个位置
mid + 1
左闭右开区间 [left,right)
while(left < right)
{
int mid = (left + right)>>1;
if(nums[mid] == target) return mid;
else if(nums[mid] > target) right = mid;
else if(nums[mid]