剑指offer面试题16(java版):数值的整数次方

welcome to my blog

剑指offer面试题16(java版):数值的整数次方

题目描述

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

class Solution {
    public double myPow(double x, int n) {
        if(x==0){
            return 0;
        }
        if(x==1){
            return 1;
        }
        //
        if(n==0){
            return 1;
        }
        if(n==1){
            return x;
        }
        //
        double res = 0;
        if(n<0){
            if(n==Integer.MIN_VALUE){
                n++;
                n = -n; //Integer.MAX_VALUE
                x = 1/x;
                return x*x * myPow(x*x, n/2);
            }else{
                n = -n;
                x = 1/x;
            }
        }
        return n%2==0? myPow(x*x, n/2) : x*myPow(x*x, n/2);
    }
}
第四次做; 力扣上的案例比牛客上多很多, 之前在牛客写的方法不能通过了; 核心: 1) core()考虑了exponent的二进制形式, 自底向上计算幂, 这是快速幂的计算方式, 是一个通用方法; 2) core2()使用分支的思想, 自顶向下思考, 自底向上实现, 注意避免重复计算; 3) 要考虑exponent==-2147483648的情况
class Solution {
    public double myPow(double x, int n) {
        //看起来应该不会出现0的0次方的情况
        //
        if(n==0)
            return 1;
        if(x==0)
            return 0;
        //特殊案例:x=1.00000, n=2147483647; 不单独处理会发生超时
        if(x==1)
            return 1;
        //记录次方的绝对值
        int tmp=0;
        //这样做会有溢出风险, 比如n原本是-2147483648, 取绝对值后发生溢出
        // tmp = Math.abs(n);
        if(n==Integer.MIN_VALUE)
            tmp=Integer.MAX_VALUE;
        else
            tmp=Math.abs(n);
        double cur = core(x, tmp);
        if(n==Integer.MIN_VALUE)
            cur = cur*x;
        return n>0 ? cur : 1/cur;
        
    }
    
    
    //通过的方法
    //分而治之; x^a = x^(a/2) * x^(a/2);  自顶向下思考, 自底向上实现
    private double core2(double x, int n){
        if(n==1)
            return x;
        double tmp = core(x, n/2);
        return n%2==0? tmp*tmp : tmp*tmp*x; 
    
    }
    
    //通过的方法
    //使用exponent的二进制形式, 这样exponent由32位构成, 最多循环32次
    private double core(double x, int n){
        double res = 1;
        while(n>0){
            //最低位为1, 更新res
            if((n&1)==1){
                res = res * x;
            }
            //更新x
            x = x*x;
            //更新n
            n = n>>1;
        }
        return res;
    }
    
    
    //递归函数逻辑: 当前数x乘上x的(n-1)次方的结果; 不能通过极端案例x=0.00001, n=2147483647, 栈溢出
    // private double core(double x, int n){
    //     if(n==0)
    //         return 1;
    //     return x*core(x,n-1);
    // }
    
    //这种计算方式太耗时, 极端案例: x=2.00000 n=-2147483648, 超时
    // private double core(double x, int n){
    //     double res = 1;
    //     while(n>0){
    //         res = res * x;
    //         n--;
    //     }
    //     return res;
    // }
    
    //分而治之; x^a = x^(a/2) * x^(a/2); 两个core()会存在大量重复, 超时, x=0.00001 n=2147483647
    // private double core(double x, int n){
    //     if(n==1)
    //         return x;
    //     int mi = n%2==0? n/2 : 1+n/2;
    //     //两个core()会产生大量重复计算
    //     return core(x, mi) * core(x, n-mi);
    // }
    
}

思路

  • 按base和exponent取值分情况讨论, 具体见注释
  • 当exponent为负数时,要注意将base取倒数,同时将exponent变成正数,表示base自乘exponent次

复杂度

第三次做, 按照底数是否为0分为两大类; 比较特殊的情况:底数为0; 指数小于0; 合适的分类讨论配合if else会让代码变简单

public class Solution {
    public double Power(double base, int exponent) {
        if(base==0){
            if(exponent<=0)
                throw new RuntimeException("底数不能为0");
            return 0.0;
        }
        double temp = multiply(base, Math.abs(exponent));
        return exponent >= 0 ? temp : 1/temp;
  }
    public double multiply(double base, int e){
        int res = 1;
        for(int i=0; i<e; i++)
            res *= base;
        return res;
    }
}
第二次做 次幂问题:按照指数和底数分情况讨论
/*
要判断指数和底数, 先按照指数分类
exponent > 0
底数没有限制

exponent ==0
底数为0
底数不为0

exponent < 0
底数为0
底数不为0(倒数)

进一步地,可以分成两种情况
exponent == 0 
exponent != 0
*/

public class Solution {
    public double Power(double base, int exponent) {
        double res  = 0;
        if(exponent == 0){
            if(base == 0)
                throw new RuntimeException();
            else
                res = 1;
        }
        else if(exponent > 0)
            res = pow(base, exponent);
        else{
            if(base == 0)
                throw new RuntimeException();
            else{
                res = 1/pow(base, -exponent);
            }
        }
        return res;
  }
    public double pow(double base, int exponent){
        double res = 1;
        for(int i=exponent; i>0; i--)
            res *= base;
        return res;
    }
}
第二次 递归求指数过程,可以减少连乘的次数
public class Solution {
    public double Power(double base, int exponent) {
        double res  = 0;
        if(exponent == 0){
            if(base == 0)
                throw new RuntimeException();
            else
                res = 1;
        }
        else if(exponent > 0)
            res = pow(base, exponent);
        else{
            if(base == 0)
                throw new RuntimeException();
            else{
                res = 1/pow(base, -exponent);
            }
        }
        return res;
  }
    //递归函数逻辑:求base的exponent幂
    public double pow(double base, int exponent){
        //base case
        if(exponent==0)
            return 1;
        //
        double res = base;
        double temp = pow(base, exponent/2);
        if((exponent & 1) == 1)
            res = base * temp * temp;
        else
            res = temp * temp;
        return res;
    }
}
public class Solution {
    public double Power(double base, int exponent) {
        /*
        底数分成两种:(1)非零, (2)0
        指数分成三种:(1)正数, (2)负数, (3)0
        具体地,
            1.底数非零时,分为两种情况
                1.1指数大于等于0:直接求次方
                1.2指数小于0:base要取倒数; 还要注意把exponent变为正数,表示base自乘exponent次
            2.底数是零时,指数需要分情况讨论
                2.1指数是正数:返回0
                2.2指数是0:返回0(这个自己定义)
                2.3指数是负数:不允许,因为0无法作为分母
        */
        double result=1;
        //1. 
        if(base != 0 ){
            //1.2
            if(exponent<0){
                exponent = -1*exponent;
                base = 1/base;
            }
            //1.1
            for(int i=0; i<exponent; i++)
                result *= base;
            return result;
        }
        //2.1, 2.2
        if(exponent>= 0)
            return 0;
        //2.3
        return 0;
  }
}

更高效的代码

思路

  • 不再采用逐个相乘的方式,也就是指数每次增加1
  • 令指数也成指数变化,比如指数依次为:1,2,4,8,…
  • base^exponent的结果可以根据exponent的奇偶分成两种情况
  • 情况一: exponent为偶数, 那么base^exponent = base^(exponent/2)*base^(exponent/2)
  • 情况二: exponent为奇数, 那么base^exponent = base^((exponent-1)/2)*base^((exponent-1)/2)*base
  • 代码中(exponent-1)/2直接写成exponent/2即可
  • 指数不断除以2,最终会变成…8,4,2,1,0 注意找好递归终止条件
  • 最后要额外注意, 在递归函数中,base是double类型, 因为指数为负数时,底数要取倒数,所以得用double类型, 这是个细节
public class Solution {
    public double Power(double base, int exponent) {
        /*
        底数分成两种:(1)非零, (2)0
        指数分成三种:(1)正数, (2)负数, (3)0
        具体地,
            1.底数非零时,分为两种情况
                1.1指数大于等于0:直接求次方
                1.2指数小于0:base要取倒数; 还要注意把exponent变为正数,作为乘几次的标志
            2.底数是零时,指数需要分情况讨论
                2.1指数是正数:返回0
                2.2指数是0:返回0(这个自己定义)
                2.3指数是负数:不允许,因为0无法作为分母
        */
        double result=1;
        //1. 
        if(base != 0 ){
            //1.2
            if(exponent<0){
                exponent = -1*exponent;
                base = 1/base;
            }
            //1.1
            return PowerCore(base, exponent);
        }
        //2.1, 2.2
        if(exponent>= 0)
            return 0;
        //2.3
        return 0;
  }
    private double PowerCore(double base, int exponent){ // 递归函数中, base是double类型, 为了处理负指数,需要把之前的base取到数
        if(exponent==0)
            return 1;
        double result = 1;
        double yinShu = PowerCore(base, exponent/2);
        if((exponent&1)==1)//如果指数是奇数,结果要多乘一个base
            result *= base;
        result = yinShu*yinShu*result;
        return result;
            
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值