LeetCode 69 x的平方 C语言实现 二分法

原题地址:x的平方根 - 力扣
题目描述

解题思路:

这道题题目说白了就是复现sort()函数。

由于返回值是整数,所以最简单的方法当然是从 0 → X 0\rightarrow X 0X依次进行判断得出答案。

但直接循环会浪费很多时间,在我们玩猜数字游戏的时候,我们都知道可以通过不断折半猜数字,很快的得到答案,而这样的思想就是二分。

而本题就是一个很好展现二分思想的一个例题。

在学习二分的时候发现了个很好用的二分查找算法模板

它将其分成了两个情况,接下来一个一个的进行讲解。(以下两个模板均来自 二分查找算法模板

该模板的算法思路:假设目标值在闭区间 [ l , r ] [l, r] [l,r] 中, 假设 M M M 是我们最后要的答案,那么这个区间就会被 M M M 分成两个部分。

而在判断的时候我们是将 M M M 放在左半部分还是右半部分,就会决定着我们的代码会是一个怎么的样子, 而这两个模板就是针对这两种情况而提供的。

版本一:

int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}

版本一是当我们将区间 [ l , r ] [l, r] [l,r] 划分成 [ l , m i d ] [l, mid] [l,mid] [ m i d + 1 , r ] [mid + 1, r] [mid+1,r] 时(既 M M M 属于右半部分的时候),其更新操作是 r = m i d r = mid r=mid 或者 l = m i d + 1 ; l = mid + 1; l=mid+1; ,计算 m i d mid mid 时不需要加1。

这个模板中为什么 l = m i d + 1 l = mid + 1 l=mid+1 呢?

是因为,当给 l l l 赋值的时候,是 m i d mid mid 不在区间的右半部分的时候。又因为答案 M M M 是在右半部分,所以可以知道 m i d mid mid 一定不是我们要的答案,因此 l = m i d + 1 l = mid + 1 l=mid+1 。(这里是满足区间相邻两个数差值为 1 1 1时的情况下 + 1 +1 +1

版本二:

int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

版本二是当我们将区间 [ l , r ] [l, r] [l,r] 划分成 [ l , m i d − 1 ] [l, mid - 1] [l,mid1] [ m i d , r ] [mid, r] [mid,r] 时(既 M M M 属于左半部分的时候),其更新操作是 r = m i d − 1 r = mid - 1 r=mid1 或者 l = m i d ; l = mid; l=mid; ,此时为了防止死循环,计算 m i d mid mid 时需要加 1 1 1

对于为什么 r = m i d − 1 r = mid - 1 r=mid1 是因为,当给 r r r 赋值的时候,是 m i d mid mid 不在区间的左半部分的时候。又因为答案 M M M 是在左半部分,所以可以知道 m i d mid mid 一定不是我们要的答案,因此 r = m i d − 1 r = mid - 1 r=mid1 。(这里是满足区间相邻两个数差值为 1 1 1时的情况下 + 1 +1 +1

m i d = l + r + 1 &gt; &gt; 1 mid = l + r + 1 &gt;&gt; 1 mid=l+r+1>>1 是怎么回事呢?

我们可以想一下,若 l + 1 = r l + 1 = r l+1=r 时,我们采用 m i d = l + r &gt; &gt; 1 mid = l + r&gt;&gt; 1 mid=l+r>>1 会出现一个什么样的情况?

∵ m i d = l + r = ( 2 ∗ l + 1 ) / 2 = l \because mid = l + r = (2 * l + 1) / 2 = l mid=l+r=(2l+1)/2=l ∴ l = m i d = l \therefore l = mid = l l=mid=l ∴ 区 间 范 围 依 然 是 [ l , r ] \therefore区间范围依然是[l, r] [l,r]

所以为了出现这种死循环的情况,我们要这样计算 m i d = l + r + 1 &gt; &gt; 1 mid = l + r + 1 &gt;&gt; 1 mid=l+r+1>>1

接下来返回我们的题目,首先我们要思考采用它是属于哪一种类型的模板。

最初我以为两个模板都可以使用,于是就直接用了第一个模板来写了一个,最后连样例都没有过…

然后思考后发现,这道题只能采用第二个模板,即答案 M M M 在左边的情况。

因为我们这道题要求的时 “由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。” 因此如果结果时 2.333 , 2.999 , 2.123 2.333, 2.999, 2.123 2.333,2.999,2.123 之类的最后输出都只能是 2 2 2

在确定了属于哪种类型后就直接套模板写代码即可。

A C AC AC代码:

int mySqrt(int x){
    int l = 0, r = x;
    while(l < r){
        int mid = l + (long long)r + 1 >> 1;
        if(mid <= x / mid)
            l = mid;
        else
            r = mid -1;
    }
    return r;
}

需要注意的是防止数据过大而导致数据溢出,上面的代码也做了防止溢出的相应处理。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值