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]
思路:
方法1 :遍历数组,记录目标值出现的第一次和最后一次的位置。
O(N)的时间复杂度无法通过这题,没有用到数组有序的条件,可以使用二分求解。
方法2 :
假设给定数组 nums = [5,7,8,8,9,10,10] ,target = 8
需要求目标值在数组中第一次和最后一次出现的位置。
如果是正常的二分查找,此时遇到目标值就返回。我们需要先找到target第一次出现的位置。
寻找左边界:
此时设置判断条件为 target <= nums[mid],
当nums[right] == num[mid] 时不返回,继续在左区间寻找。
代码示例:
//求目标值左边界
int lowbound(int* nums, int numsSize, int target)
{
int left = 0;
int right = numsSize - 1;
while(left <= right)
{
int mid = left + (right - left);
//找到target时不返回,继续在左区间查找。
if(target <= nums[mid])
{
right = mid - 1;
}
else if(target > nums[mid])
{
left = mid + 1;
}
}
//循环结束left位于左边界
return left;
}
过程图示
当循环结束时,left位于目标值左边界。
寻找右边界:
和寻找左边界相反,如果寻找target时,继续往右区间寻找。
target >= nums[mid]
int highbound(int* nums, int numsSize, int target)
{
int left = 0;
int right = numsSize - 1;
while(left <= right)
{
int mid = left + (right - left);
if(target < nums[mid])
{
right = mid - 1;
}
//继续往右区间查找。
else if(target >= nums[mid])
{
left = mid + 1;
}
}
//循环结束right位于右边界
return right;
}
搜索过程:
循环停止时,right 停留在下边界。
总体:
int* searchRange(int* nums, int numsSize, int target, int* returnSize)
{
if(nums == NULL) return NULL;
int left = lowbound(nums,numsSize,target);//求左边界
int right = highbound(nums,numsSize,target);//求右边界
int* retArr = (int*)malloc(sizeof(int) * 2);
*returnSize = 2;
//left>right时,说明找不到目标值
if(left > right)
{
retArr[0] = -1;
retArr[1] = -1;
return retArr;
}
retArr[0] = left;
retArr[1] = right;
return retArr;
}
int lowbound(int* nums, int numsSize, int target)
{
int left = 0;
int right = numsSize - 1;
while(left <= right)
{
int mid = left + (right - left);
if(target <= nums[mid])
{
right = mid - 1;
}
else if(target > nums[mid])
{
left = mid + 1;
}
}
return left;
}
int highbound(int* nums, int numsSize, int target)
{
int left = 0;
int right = numsSize - 1;
while(left <= right)
{
int mid = left + (right - left);
if(target < nums[mid])
{
right = mid - 1;
}
else if(target >= nums[mid])
{
left = mid + 1;
}
}
return right;
}