剑指offer 16 数值的整数次方

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000

示例 2:

输入:x = 2.10000, n = 3
输出:9.26100

示例 3:

输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25

提示:

-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目很简单,但是这道题需要充分考虑边界以及特殊情况,比如x为负的情况,n为负的情况,这些情况都需要考虑进去。

如果这些情况都考虑到了,很容易写下如下题解:

class Solution {
public:
    double myPow(double x, int n) {
         bool flage = (n >= 0) ? true : false;
         double res = 1;
         if(x == 0) return 0;
         if(x == 1) return 1;
         int abs_n = flage ? n : -n;
         //int falge_res = ( abs_n%2 == 0) ? 1 :( x >= 0) ? 1 : -1;
         if(flage){
             while(abs_n--){
                 res  = res*x;
             }
         }
         else{
             while(abs_n--)
             res = res*(1/x);
         }
         
         return res;//*falge_res ; 
    }
};

但是有个特殊的测试用例过不去:

2.00000
-2147483648

这个测试用例为什么过不去呢?
因为数值在计算机中存储是用补码,稍微有点计算机基础的人都会知道,int32_t 的 正数和负数的范围是不一致,因此,可以先用long型变量拿到n,再对long型数取反就不会溢出了。

long b = n;
.....
int abs_n = flage ? b: -b;

这时候,提交,发现超时了,因为此时的复度是O(n)的复杂度,指数越大,耗时越长,看来题目要求我们降低复杂度。

这时候我们就想起来了二分法,

a^n = a^(n/2)*a^(n/2) n为偶数
a^n = a^((n-1)/2)*a^((n-1)/2)*a n为奇数

如此一来,复杂度降到为了O(log n)。

需要注意,如果n为偶数,除法可以用位移操作,同样,如果n为奇数,(n-1)>>1 == n>>1,所以,用位移的操作的时候可以不减一

递归的思路比较简单,

解释两点,
1:为了简化判断,索性就在函数入口做判断,把数据统一处理成正数来做
2:这里把递归函数单独提出来,方便理解

如下:

class Solution {
public:
    double kernel(double x, long n){
        if( n == 0) return 1;
        if( n == 1) return x;
        double res = kernel(x,n>>1);
        res = res*res;
        if((n & 0x1) == 1)
           res = res*x;
        return res;   
    }
    double myPow(double x, int n) {
         long b = n;
         bool flage = (b >= 0) ? true : false;
         double res = 1.0;
         if(x == 0) return 0;
         if(x == 1) return 1;
         long abs_n = flage ? b : -b;
         if(!flage){
             x = 1/x;
             abs_n = -b;
         }
         res = kernel(x,abs_n);
         return res;
    }
};

递归好理解,但是程序每次递归调用都会一起一定的开销,所以,这里也提供了迭代的方法供参考。

class Solution {
public:
    double myPow(double x, int n) {
         long b = n;
         bool flage = (b >= 0) ? true : false;
         double res = 1.0;
         if(x == 0) return 0;
         if(x == 1) return 1;
         
         long abs_n = flage ? b : -b;
         
         if(!flage){
             x = 1/x;
             abs_n = -b;
         }
        while(abs_n > 0){
            if( (abs_n&1) == 1) res*=x;
            x = x*x;
            abs_n = abs_n >> 1;
        }
         return res;
    }
};

前面的步骤一样,也是需要解释两点,
1:n>>1一定要赋值给n,直接n>>1不带副作用,n的数值不会变
2:这里当n为偶数时,x=x*x,并没有赋值给res,会不导致漏掉呢?答案是不会的,因为偶数除2必定为奇数,所以res的数值在abs_n=1的时候更新到。

我们可以先写出递归代码, 再改成迭代的形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值