题目:34. 在排序数组中查找元素的第一个和最后一个位置

题目

给你一个按照非递减顺序排列的整数数组 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;
}

总结

这道题目目前为止花的时间比较多,自己对于边界的处理还不到位,左右边界分开处理的办法便于自己理解。同时自己很多时间都用在了处理极限值上,比如输入的数组是空数组等等,这就意味着自己在写的时候就要把很多因素给考虑进去,要考虑全面。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值