Java版剑指offer编程题第12题--数值的整数次方

跟learnjiawa一起每天一道算法编程题,既可以增强对常用API的熟悉能力,也能增强自己的编程能力和解决问题的能力。算法和数据结构,是基础中的基础,更是笔试的重中之重。

  • 不积硅步,无以至千里;
  • 不积小流,无以成江海。

题目描述

no12
Java版剑指offer编程题第12题–数值的整数次方: 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0

我的想法

  • 直接累乘,如果exponent指数为n,你就要累乘n-1次,时间复杂度记为O(n),这里n趋于无穷大,所以省去“-1”。
  • 采用二分思想,对于最朴素的想法,77=49,497=343,… 一步一步算,共进行了9次乘法。这样算无疑太慢了,尤其对计算机的CPU而言,每次运算只乘上一个个位数,无疑太屈才了。这时我们想到,也许可以拆分问题,即递归快速幂
    先算7的5次方,即77777,再算它的平方,共进行了5次乘法。但这并不是最优解,因为对于“7的5次方”,我们仍然可以拆分问题。
    先算77得49,则7的5次方为4949*7,再算它的平方,共进行了4次乘法。
  • 递归虽然简洁,但会产生额外的空间开销。我们可以把递归改写为循环,来避免对栈空间的大量占用,也就是非递归快速幂。换一个角度来引入非递归的快速幂。
    还是7的10次方,但这次,我们把10写成二进制的形式,也就是1010 。现在我们要计算7^(1010) ,可以怎么做?
    我们很自然地想到可以把它拆分为7 ^(1000)*7 ^ (0010) . 实际上,对于任意的整数,我们都可以把它拆成若干个 7 ^ (100…) 的形式相乘。而这些7 ^ (100…),恰好就是 7 ^ 1,7 ^ 2,7 ^ 4。

解题方法1

 /**
     * 直接累乘,如果exponent指数为n,你就要累乘n-1次
     * 时间复杂度记为O(n),这里n趋于无穷大,所以省去“-1”
     * */
    public static double Power(double base, int exponent) {
        double result=1;
        int abs_exponent;
        /**
         * 分别考虑指数为0或底数为0的特殊情况
         * */
        if(exponent == 0){
            return 1;
        }
        if(base == 0){
            return 0;
        }
        //指数大于0
        if(exponent > 0){
            abs_exponent = exponent;
            for(int i = 0; i < abs_exponent; i++){
                result=result*base;
            }
            return result;
        }else{
            //指数小于0,先将指数取反,然后返回计算结果的倒数
            abs_exponent = -exponent;
            for(int i = 0; i < abs_exponent; i++){
                result=result*base;
            }
            return 1/result;
        }

    }

解题方法2

 /**
     * 解法2:递归快速幂,非递归快速幂,时间复杂度:O(logn)
     * 1. 最朴素的想法,7*7=49,49*7=343,... 一步一步算,共进行了9次乘法。
     *这样算无疑太慢了,尤其对计算机的CPU而言,每次运算只乘上一个个位数,无疑太屈才了。这时我们想到,也许可以拆分问题。
     * 2. 先算7的5次方,即7*7*7*7*7,再算它的平方,共进行了5次乘法。
     *但这并不是最优解,因为对于“7的5次方”,我们仍然可以拆分问题。
     * 3. 先算7*7得49,则7的5次方为49*49*7,再算它的平方,共进行了4次乘法。*/
    public static double Power2(double base, int exponent) {
        double result=1;
        int abs_exponent;
        /**
         * 分别考虑指数为0或底数为0的特殊情况
         * 递归出口
         * */
        if(exponent == 0){
            return 1;
        }
        if(base == 0){
            return 0;
        }
        //指数大于0
        if(exponent > 0){
            abs_exponent = exponent;
            //指数为奇数
            if(abs_exponent % 2 ==1){
                return Power2(base,abs_exponent-1)*base;
            }else{
                //指数为偶数
                double temp = Power2(base, abs_exponent/2);
                return temp*temp;
            }
        }else{
            //指数小于0,先将指数取反,返回计算结果的倒数
            abs_exponent = -exponent;
            if(abs_exponent % 2 ==1){
                //指数为奇数
                return 1/(Power2(base,abs_exponent-1)*base);
            }else{
                //指数为偶数
                double temp = Power2(base, abs_exponent/2);
                return 1/(temp*temp);
            }
        }

    }

解题方法3

	//非递归快速幂,时间复杂度:O(logn)
 public static double Power3(double base, int exponent) {
        double result=1;
        int abs_exponent;
        /**
         * 分别考虑指数为0或底数为0的特殊情况
         * 递归出口
         * */
        if(exponent == 0){
            return 1;
        }
        if(base == 0){
            return 0;
        }
        //存储中间计算结果
        double curr = base;
        //考虑指数的正负性
        if(exponent < 0){
            abs_exponent = -exponent;
        }else{
            abs_exponent = exponent;
        }
       // 注意递归和非递归的快速幂的关系,方便理解
        while(abs_exponent > 0) {
            //这里的位运算相当于递归快速幂中的abs_exponent % 2 ==1
                if ((abs_exponent & 1) == 1) {
                    result *= curr;
                }
                curr *= curr;
                //右移,相当于 /2
                abs_exponent = abs_exponent >> 1;
        }
        if(exponent > 0){
            return result;
        }else{
            return 1/result;
        }
    }

代码测试

package com.learnjiawa.jzoffer;

/**
 * @author learnjiawa
 * 2019-12-11-10:29
 */
public class Solution12 {
    public static void main(String[] args) {
        double base = 7.0;
        int exponent = 10;
        System.out.println("累乘法计算数字"+ base + "的" + exponent + "次幂是"+Power(base,exponent));
        System.out.println("递归快速幂计算数字"+ base + "的" + exponent + "次幂是"+Power(base,exponent));
        System.out.println("非递归快速幂计算数字"+ base + "的" + exponent + "次幂是"+Power(base,exponent));
    }
    /**
     * 直接累乘,如果exponent指数为n,你就要累乘n-1次
     * 时间复杂度记为O(n),这里n趋于无穷大,所以省去“-1”
     * */
    public static double Power(double base, int exponent) {
        double result=1;
        int abs_exponent;
        /**
         * 分别考虑指数为0或底数为0的特殊情况
         * */
        if(exponent == 0){
            return 1;
        }
        if(base == 0){
            return 0;
        }
        //指数大于0
        if(exponent > 0){
            abs_exponent = exponent;
            for(int i = 0; i < abs_exponent; i++){
                result=result*base;
            }
            return result;
        }else{
            //指数小于0,先将指数取反,然后返回计算结果的倒数
            abs_exponent = -exponent;
            for(int i = 0; i < abs_exponent; i++){
                result=result*base;
            }
            return 1/result;
        }

    }
    /**
     * 解法2:递归快速幂
     * 1. 最朴素的想法,7*7=49,49*7=343,... 一步一步算,共进行了9次乘法。
     *这样算无疑太慢了,尤其对计算机的CPU而言,每次运算只乘上一个个位数,无疑太屈才了。这时我们想到,也许可以拆分问题。
     * 2. 先算7的5次方,即7*7*7*7*7,再算它的平方,共进行了5次乘法。
     *但这并不是最优解,因为对于“7的5次方”,我们仍然可以拆分问题。
     * 3. 先算7*7得49,则7的5次方为49*49*7,再算它的平方,共进行了4次乘法。*/
    public static double Power2(double base, int exponent) {
        double result=1;
        int abs_exponent;
        /**
         * 分别考虑指数为0或底数为0的特殊情况
         * 递归出口
         * */
        if(exponent == 0){
            return 1;
        }
        if(base == 0){
            return 0;
        }
        //指数大于0
        if(exponent > 0){
            abs_exponent = exponent;
            //指数为奇数
            if(abs_exponent % 2 ==1){
                return Power2(base,abs_exponent-1)*base;
            }else{
                //指数为偶数
                double temp = Power2(base, abs_exponent/2);
                return temp*temp;
            }
        }else{
            //指数小于0,先将指数取反,返回计算结果的倒数
            abs_exponent = -exponent;
            if(abs_exponent % 2 ==1){
                //指数为奇数
                return 1/(Power2(base,abs_exponent-1)*base);
            }else{
                //指数为偶数
                double temp = Power2(base, abs_exponent/2);
                return 1/(temp*temp);
            }
        }

    }

    public static double Power3(double base, int exponent) {
        double result=1;
        int abs_exponent;
        /**
         * 分别考虑指数为0或底数为0的特殊情况
         * 递归出口
         * */
        if(exponent == 0){
            return 1;
        }
        if(base == 0){
            return 0;
        }
        //存储中间计算结果
        double curr = base;
        //考虑指数的正负性
        if(exponent < 0){
            abs_exponent = -exponent;
        }else{
            abs_exponent = exponent;
        }
       // 注意递归和非递归的快速幂的关系,方便理解
        while(abs_exponent > 0) {
            //这里的位运算相当于递归快速幂中的abs_exponent % 2 ==1
                if ((abs_exponent & 1) == 1) {
                    result *= curr;
                }
                curr *= curr;
                //右移,相当于 /2
                abs_exponent = abs_exponent >> 1;
        }
        if(exponent > 0){
            return result;
        }else{
            return 1/result;
        }


    }
}

总结

题目的考察知识点是快速幂算法,可以选择使用递归或者位运算,减少运算次数,减小时间复杂度,明天见哦!

参考文献

[1]程杰. 大话数据结构. 北京:清华大学出版社, 2011.

更多

对我的文章感兴趣,点个关注是对我最大的支持,持续更新中…
关注微信公众号LearnJava:
LearnJava

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值