每日一题
在排序数组中查找元素的第一个和最后一个位置
思路
看到这是一个非递减顺序排序的整数数组,且题目要求的时间复杂度为O(logn),我们可以知道,这题要用二分查找
但这题不是简单的找到目标元素target就行了,而是要给出它在数组中的开始位置和结束位置。而我们知道·,如果一个数再一个有序数组中重复出现,那么如果使用二分查找,查找到的元素下标是不确定的(可能是第一个,可能是中间的,可能是最后一个)
因此我们就需要对二分查找算法进行处理,找到target的开始位置left,结束位置right
我们知道,普通二分法找到的条件是nums[mid]=target,如下图:
那么,如果我们当nums[mid]=target时不退出循环而是继续进行呢?
假设我们要找结束位置right:显然结束位置应该在mid或mid右边的位置,那么我们就应该继续向右检索,怎么向右检索呢?我们知道二分查找中,当nums[mid]<target时,left就等于mid+1,mid自然也就右移了,这样不就实现了向右检索吗?如下图:
假设我们要找开始位置left:同样,开始位置应该在mid或mid左边的位置,所以当nums[mid] >= target时,right就等于mid-1,这样也就实现了mid的左移。如下图:
实现代码
方法一
//找到开始位置下标
int leftRange(int *nums,int numsSize,int target)
{
int left = 0;
int right = numsSize - 1;
int range;
while(left <= right)
{
int mid = (right - left) / 2 + left; //防止加法发生整形溢出
if(nums[mid] >= target) //只要mid的左边有target元素,就向左检索
{
right = mid - 1;
range = right; //存储可能成为开始位置的下标
}
else
left = mid + 1;
}
return range+1; //最后一次right=mid-1使得left>right,可以认为这是一个无效运算,因此需要将-1抵消
}
//找到结束位置下标
int rightRange(int *nums,int numsSize,int target)
{
int left = 0;
int right = numsSize - 1;
int range;
while(left <= right)
{
int mid = (right - left) / 2 + left;
if(nums[mid] > target)
right = mid - 1;
else //只要mid的右边有target元素,就向右检索
{
left = mid + 1;
range = left;
}
}
return range-1; //最后一次left=mid+1使得left>right,可以认为这是一个无效运算,因此需要将1抵消
}
int* searchRange(int* nums, int numsSize, int target, int* returnSize){
*returnSize = 2;
int *number = (int *)malloc(sizeof(int) * (*returnSize));
int left = 0;
int right = numsSize - 1;
int flag = 0;
while(left <= right) //首先判断target是否存在于数组中
{
int mid = (right - left) / 2 + left;
if(nums[mid] < target)
left = mid + 1;
else if(nums[mid] > target)
right = mid - 1;
else
{
flag = 1;
break;
}
}
if(flag) //如果存在
{
number[0] = leftRange(nums,numsSize,target);
number[1] = rightRange(nums,numsSize,target);
}
else //如果不存在
number[0] = number[1] = -1;
return number;
}