题目
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
解题思路
错误解答
对于这道题,很明显是采用二分查找的思想,但是这个时候数组里面是可能有重复元素的,我最开始的思路就是找到其中一个目标值,在目标值的位置上以其为中心向左向右寻找边界。
以下是我的实现代码
int* searchRange(int* nums, int numsSize, int target, int* returnSize)
{
returnSize = malloc(sizeof(int) * 2);
int left = 0;
int right = numsSize - 1;
int mid;
int begin ;
int end;
while(left <= right)
{
mid = (left + right) / 2;
if(target < nums[mid])
{
right = mid - 1;
}
else if(target > nums[mid])
{
left = mid + 1;
}
else
{
begin = end = mid;
while(nums[begin] == target)
{
begin--;
}
while(nums[end] == target)
{
end++;
}
returnSize[0] = ++begin;
returnSize[1] = --end;
}
}
return returnSize;
}
但是提交发现,超出时间限制,只好看题解寻求其他的解体方法。
正确解答
以上的解法是超出时间限制不符合题意的,而且看了相关资料也发现之前自己写的二分查找也存在疏漏:
mid = (left + right) / 2 的写法是存在溢出风险的,要将其修改为mid = left + (right - left) / 2
int* searchRange(int* nums, int numsSize, int target, int* returnSize)
{
int leftBoder = getLeft(nums, numsSize, target);
int rightBoder = getRight(nums, numsSize, target);
int *returnArray=(int *)malloc(sizeof(int)*2);
*returnSize = 2;
if (rightBoder == -2 || leftBoder == -2 || (rightBoder - leftBoder <= 1))
{
returnArray[0] = -1;
returnArray[1] = -1;
}
else
{
returnArray[0] = leftBoder + 1;
returnArray[1] = rightBoder - 1;
}
return returnArray;
}
int getLeft(int* nums, int numsSize, int target)
{
int left = 0;
int right = numsSize - 1;
int leftBoder = -2;
int mid = -1;
while (left <= right)
{
mid = left + (right - left) / 2;
if (nums[mid] >= target)
{
right = mid - 1;
leftBoder = right;
}
else
{
left = mid + 1;
}
}
return leftBoder;
}
int getRight(int* nums, int numsSize, int target)
{
int left = 0;
int right = numsSize - 1;
int rightBoder = -2;
int mid = -1;
while (left <= right)
{
mid = left + (right - left) / 2;
if (nums[mid] <= target)
{
left = mid + 1;
rightBoder = left;
}
else
{
right = mid - 1;
}
}
return rightBoder;
}
上述思路是将左右边界拆分俩函数进行处理,便于理解。但可以发现代码很多复用的,于是改进有了以下的代码。
int* searchRange(int* nums, int numsSize, int target, int* returnSize)
{
int leftBoder = getBoder(nums, numsSize, target) + 1;
int rightBoder = getBoder(nums, numsSize, target + 1);
int *returnArray=(int *)malloc(sizeof(int)*2);
*returnSize = 2;
if (rightBoder - leftBoder <= -1)
{
returnArray[0] = -1;
returnArray[1] = -1;
}
else
{
returnArray[0] = leftBoder;
returnArray[1] = rightBoder;
}
return returnArray;
}
//返回第一个>=target的位置
int getBoder(int* nums, int numsSize, int target)
{
int left = 0;
int right = numsSize - 1;
int mid = -1;
while (left <= right)
{
mid = left + (right - left) / 2;
if (nums[mid] >= target)
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
return right;
}
总结
这道题目目前为止花的时间比较多,自己对于边界的处理还不到位,左右边界分开处理的办法便于自己理解。同时自己很多时间都用在了处理极限值上,比如输入的数组是空数组等等,这就意味着自己在写的时候就要把很多因素给考虑进去,要考虑全面。