LeetCode69_Sqrt(x)

求开根号之后的值,本身实现不难,但是运行速度有待提高。

题目:

Implement int sqrt(int x).

Compute and return the square root of x, where x is guaranteed to be a non-negative integer.

Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned.

Example 1:

Input: 4
Output: 2

Example 2:

Input: 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since 
             the decimal part is truncated, 2 is returned.

最开始直接用了sqrt函数。。。

int mySqrt(int x) {
    int t;
    t=(int)sqrt(x);
    return t;
}
然后发现其实人家是让你自己实现这个函数。。

于是用1~x/2的for循环遍历,写了自己的一版代码,实现起来很慢。。

int mySqrt(int x) {
    int i;
    if(x<0){return -1;}
    if(x==0){return 0;}
    if(x==1){return 1;}
    for(i=1;i<=x/2;i++){
        if(pow(i,2)==x||(pow(i,2)<x&&pow(i+1,2)>x)){break;}
    }
    return i;
}

因为对于算法不是特别了解,所以直接百度了其他人的代码答案,大概有两个思路:

1.二分法查找

对于一个非负数n,它的平方根取整 ⌊√x⌋ ≤ (⌊x/2⌋+1),如下图所示,有x=1、2、4共3个整数交点,x>4以后⌊√x⌋ 恒小于 (⌊x/2⌋+1).

由于int sqrt(int x)接受的参数与返回值均为int型,故⌊√x⌋ ≤ (⌊x/2⌋+1)即等价于强数据类型语言(比如:C++、C、Java等)中的√x(目标值)≤ x/2+1 (x为自然数,非负整数). 于是在[0, x/2+1]这个范围内进行二分搜索,可以求出n的int型平方根,mid=(low+up)/2,其初值为x/2,结果应在[low, up]的mid或up处取得。如果用弱数据类型的语言(比如:PHP、Python、JavaScript等)实现此方法,需先自行ceiling或ceil进行下取整!

但此法不适用于double,因为此法利用了int型的特点。

2.牛顿迭代法

  Newton's Method(牛顿切线法)是由艾萨克·牛顿在《流数法》(Method of Fluxions,1671年完成,在牛顿死后的1736年公开发表)中最早提出的。约瑟夫·拉弗森也曾于1690年在Analysis Aequationum中提出此方法。它是牛顿在17世纪提出的一种在实数域和复数域上近似求解方程的方法。 

 

因为对于自己而言二分法比较好理解,所以选择二分法作为参考代码,具体如下:

1.为什么初始范围是[1, (x/2) + 1]?

要实现一个sqrt函数,可以使用二分法,首先确定一个范围[begin, end],这个范围的中间数mid,看mid的平方是否等于x,如果相等,则返回mid,如果不等则缩小[begin,end]的范围,为原来的一半。这里的初始范围可以是[1, x],也可以是更精确一些的[1, (x/2) + 1]。(因 (x/2) + 1 的平方等于 x+1+(x^2/4),它一定大于x,所以,x的平方根一定在[1, (x/2) + 1]范围内)

2.如何缩小范围?

范围是[low,high],最开始为[1, (x/2) + 1]

取low和high的中间值mid=low + (high - low) / 2,如果中间值mid的平方小于x,那就在后半段;如果大于x,那就在前半段。

3.如何确定是哪个值?

确定具体值是靠着缩小范围得到的最终mid值,这个值应该满足mid^2=x,或者mid^2<x且(mid+1)^2>x,两个或关系的条件合并起来应该是mid^2<=x<(mid+1)^2,返回这个mid值就是要求的开方值。

具体代码如下:

int mySqrt(int x) {
    if(x == 0) return 0;
    int low = 1, high = x / 2 + 1;
    while(low <= high){//终止循环的条件是low和high两个值汇合,等于的时候仍然应该循环,否则覆盖不全面,所以只有low>high跳出循环,两个值的改变不再是++;而是用中间值的替换其中一个值(即不断的取前半段或者后半段)
        int mid = low + (high - low) / 2;//mid取中间值,因为中间值随着high和low改变,所以一定要写在while循环内部
        if(mid > x / mid) high = mid - 1;//如果mid^2>x则取前半段,此时high直接等于mid-1,而不是等于mid
        else{
            if(mid + 1 > x / (mid + 1)) return mid;//正常的return返回值
            else low = mid + 1;//如果mid^2<=x则取后半段,令low = mid + 1而不是mid,在这之前,插入对mid+1值的判断,如果满足mid^2<=x<(mid+1)^2则直接输出该mid值
        }
    }
    return -1;//异常
}

java版本与这个思路相同。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值