渐进刷题日记:数组篇 二分法的应用

在这里插入图片描述


前言

渐进刷题记录第一天,原本以为零零散散刷了很多题,面对一个基础的二分算法,难道还有什么难度吗?没想到现实给我当头一棒。1. 关于何时 left <= right , 何时left < right, 我尽然找不到一个合理的解释,2. 当数组出现重复的元素时,又怎么用二分法来确定区间?3.……经历了半天的搜寻与刷题,我对此才有了答案。


题目解答

二分法的使用前提:该数组为有序数组(一般题目不涉及重复元素)

1.1 二分法

二分法
在这里插入图片描述

这题是一个标准的二分法题目,代码很简单,我列出这题是为了说明三个问题
本题代码:
在这里插入图片描述

解释:
问题1:取决于区间的定义。二分法本质是将一个区间不断平分,最后确定到一个数或找不到。因此需要每一次划分后的区间应该满足区间的定义
注:区间的定义:一个区间的形式有4种情况,题目一般都是前面两种。

  1. 前闭后闭 [left , right]
  2. 前闭后开 [left , right)
  3. 前开后闭 (left , right]
  4. 前开后开 (left , right)

本题很明显是属于第一种情况(因为target可能是里面的任何数)
在第一种情况循环while(left <= right)里面就是 <=,因为要满足区间的定义,在前闭后闭的情况下,存在left = right的情况,比如: [1,1] 即1,是存在的,故while循环要包括二者相等的情况。
问题3:正是在前闭后闭的情况下,当nums[middle] <或 > target时 这是不是表明下标为middle的元素肯定不是我们要找的,这时对left或right进行调整,如果改为left = middle由于是前闭后闭middle将被包括在折半后的区间里,而middle明显不是我们需要的left = middle + 1这样便将middle排除在外.
所以当区间为[left, right)时while(left < right),left = middle+1,right = middle此时right不被包括在其中。
问题2middle,left,right都是int类型,如果写成middle = (left+right)/2则可能left+right的值超过int类型,造成溢出,写成middle = left + (right-left)/2可以解决溢出问题。

1.2 搜索插入位置

搜索插入位置
在这里插入图片描述

本题在基础的二分法上加上返回应当插入的位置。

代码如下

int searchInsert (int* nums, int numsSize, int target){
    int left = 0;
    int right = numsSize - 1;
    while(left <= right) {
        int middle = left + (right-left)/2;
        if(nums[middle] > target)
            right = middle - 1;
        else if(nums[middle] < target)
            left = middle + 1;
        else 
            return middle;
    }
    return left;
}

关于返回插入的位置为什么为 left?这可以通过画图来解释。
如果target不在该数组内,查找的结果总是right最后为最小区间的左端,left最后为最小区间的右端
如:最后right指向3,left指向5,【3,5】为能找的最小区间
在这里插入图片描述

1.3 x的平方根

x的平方根
在这里插入图片描述

本题也是用二分法求解,相比于前面给出了明确的数组,本题的数组则是隐藏的。
根据x的范围,x的平方根是在 [0, x] (为闭区间是因为x=0或1的情况)。所以相当于是在一个元素为[0,1,2,3……x]的有序且连续的数组寻找满足条件的值,这思路就相当于把上面一题搜索插入位置中返回插入位置,改为返回插入位置的左相邻位置
注意:本题middle*middle存在溢出,因此改为了long long类型,当然还有另一种写法middle < x/middle但我写的代码不能这样改,因为存在 /0(除0的情况).

int mySqrt(int x) {
    int left = 0;
    int right = x;
    while(left <= right)
    {
        long long middle = left + (right - left)/2;
        if(middle*middle < x) {
            left = middle + 1;
        }
        else if(middle*middle > x) {
            right = middle - 1;
        }
        else {
            return middle;
        }
    }
    return right;
}

评论区有一个更简洁的代码,可以使用 middle > x/middle,

int mySqrt(int x) 
    {
        if(x == 1)
            return 1;
        int left = 0;
        int right = x;
        while(right-left>1)
        {
            int middle = left + (right-left)/2;
            if(x/middle < middle)
                right = middle;
            else
                left = middle;
        }
        return left;
    }

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

在排序数组中查找元素的第一个和最后一个位置
本题难度就比前几题大了很多。
在这里插入图片描述

本题依然用二分法求解,难点在于涉及重复元素
解释在代码下方:

int GetLeftBorder(int* nums, int numsSize, int target) {
    int left = 0;
    int right = numsSize - 1;
    int flag = 1;
    while(left <= right) {
        flag = 0;
        int mid = left + (right-left)/2;
        if (nums[mid] >= target) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    return flag ? -1 : left;
}
int GetRightBorder(int* nums, int numsSize, int target) {
    int left = 0;
    int right = numsSize - 1;
    int flag = 1;
    while(left <= right) {
        flag = 0;
        int mid = left + (right-left)/2;
        if(nums[mid] <= target) {
            left = mid+1;
        }
        else {
            right = mid-1;
        }
    }
    return flag ? -1 : right;
}
int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
    int LeftTarget = GetLeftBorder(nums,numsSize,target);
    int RightTarget = GetRightBorder(nums,numsSize,target);
    int* ans = (int*)malloc(sizeof(int)*2);
    ans[0] = -1;
    ans[1] = -1;
    *returnSize = 2;
    if(LeftTarget != -1 && RightTarget != -1 
    && LeftTarget <= RightTarget) {
      ans[0] = LeftTarget;
      ans[1] = RightTarget;
    }
    return ans;
}

思路:用二分法分别确定最左边的target和最右边的target
首先:确定区间的定义:前闭后闭。因为要查询整个数组,且存在数组只有一个元素的情况,即结果为[0,0]。
然后:分析有多少种情况:

  1. target在该数组中如示例1,则最后RightTarget >= LeftTarget
  2. target数组有元素,但不存在target,如示例2,则最后LeftTarget > RightTarget
  3. 数组为空,则不会进行查找,如示例3,则LeftTatget/RightTarget为-1

注:1中为什么是 >=,因为存在 {1},1;结果为[0,0],可以相等。
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值