69. x 的平方根 AND 34. 在排序数组中查找元素的第一个和最后一个位置

这篇博客介绍了如何使用二分查找算法在排序数组中查找目标元素的第一个和最后一个位置。首先尝试通过一次二分查找找到目标值的最后出现位置,然后回溯找到第一个出现的位置。原始代码存在效率问题,经过优化,将二分查找应用于两个子问题,提高了时间复杂度至O(logn),解决了超时问题。

69. x 的平方根

思路:运用二分查找的方法,将上下界分别定为0和x。循环内仅分2种情况,直至不满足循环的条件而退出循环。

int mySqrt(int x){
    if(x == 0)
        return x;
    int Left,Right,mid,index;
    Left = 0; Right = x; index = -1;
    while(Left <= Right){
        mid = Left + (Right - Left) / 2;
        if((long long)mid*mid <= x){//注意范围,避免溢出
            index = mid;
            Left = mid + 1;
        }
        else 
            Right = mid - 1;         
    }
    return index;
}

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

一开始我的思路是找到target最后一次出现的下标,然后利用for循环往前找,知道遇到不是target的数(这样做是因为给的数组是按升序排列的),代码如下:

int* searchRange(int* nums, int numsSize, int target, int* returnSize){
    int left,right,mid,startindex,endindex;
    left = 0; right = numsSize-1; startindex = endindex = -1;
    int *indexarray = malloc(sizeof(int) * 2);
    *returnSize = 2;
    while(left <= right){//找到最后一次出现的下标的后一位
		mid = left + (right - left) / 2;
		if(nums[mid] >= (target + 1))
		    right = mid;
		else
            left = mid + 1;	
	}
    endindex = left;
    if(endindex != -1){//if成立说明存在target
        endindex -= 1;
        startindex = endindex;
        for(int i = endindex-1; i>=0; i--){
            if(nums[i] != target)
                break;
            startindex = i;
        }
        indexarray[0] = startindex;
        indexarray[1] = endindex;
    }
    else{
        indexarray[0] = -1;
        indexarray[1] = -1;
    }    
    return indexarray;
}

这样做样例估计都能过,但超时了,问题大概率是出在后面的for循环上(最坏的情况是数组所有的数都是target目标值,这样一来时间复杂度就为O(n),而题目要求时间复杂度为O(log n)。

所以我不得不分别用二分法来求target第一次出现的下标target最后一次出现下标的后一位,用两个函数来实现,代码如下:

int* searchRange(int* nums, int numsSize, int target, int* returnSize){
    int left,right,mid,startindex,endindex; 
    int *indexarray = malloc(sizeof(int) * 2);
    *returnSize = 2;
    indexarray[0] = indexarray[1] = -1;
    startindex = FirstIndex(nums,numsSize,target);
    endindex = LastIndex(nums,numsSize,target) - 1;
    if(startindex == numsSize || nums[startindex] != target)//没找到的情况
        return indexarray;
    indexarray[0] = startindex;
    indexarray[1] = endindex;
    return indexarray;
}
//找到target第一次出现的下标
int FirstIndex(int* nums, int numsSize, int target){
    int left,right,mid;
    left = 0; right = numsSize - 1;
    while(left <= right){
		mid = left + (right - left) / 2;
		if(nums[mid] >= target)
		    right = mid;
		else
            left = mid + 1;	
	}
    return left;
}
///找到target最后一次出现下标的后一位
int LastIndex(int* nums, int numsSize, int target){
    int left,right,mid;
    left = 0; right = numsSize - 1;
    while(left <= right){
		mid = left + (right - left) / 2;
		if(nums[mid] > target)//与上面的差别:相等的情况放在else里了
		    right = mid;
		else
            left = mid + 1;	
	}
    return left;
}

然而这样又又超时了!(淦!)检查了、修改了不知道多少次之后终于被我试出来了!

把两个子函数里的right初值改为numsSizewhile的条件的等号拿掉,就行了!所以正确代码为:

int* searchRange(int* nums, int numsSize, int target, int* returnSize){
    int left,right,mid,startindex,endindex; 
    int *indexarray = malloc(sizeof(int) * 2);
    *returnSize = 2;
    indexarray[0] = indexarray[1] = -1;
    startindex = FirstIndex(nums,numsSize,target);
    endindex = LastIndex(nums,numsSize,target) - 1;//得到的是target最后一次出现下标的后一位
    if(startindex == numsSize || nums[startindex] != target)
        return indexarray;
    indexarray[0] = startindex;
    indexarray[1] = endindex;
    return indexarray;
}
//找到target第一次出现的下标
int FirstIndex(int* nums, int numsSize, int target){
    int left,right,mid;
    left = 0; right = numsSize;
    while(left < right){
		mid = left + (right - left) / 2;
		if(nums[mid] >= target)
		    right = mid;
		else
            left = mid + 1;	
	}
    return left;
}
///找到target最后一次出现下标的后一位
int LastIndex(int* nums, int numsSize, int target){
    int left,right,mid;
    left = 0; right = numsSize;
    while(left < right){
		mid = left + (right - left) / 2;
		if(nums[mid] > target)//与上面的差别:相等的情况放在else里了
		    right = mid;
		else
            left = mid + 1;	
	}
    return left;
}

原理暂时还没搞懂......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值