剑指offer-数值的整数次方

本文介绍了如何使用位运算和递归来求解double类型base的int类型指数exponent次方的问题。详细讨论了边界条件、快速幂思想以及递归方法,并给出了代码实现。特别强调了快速幂的效率以及利用位运算获取二进制每位数字的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

  • 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
  • 地址:牛客链接

解决方法

  • 这是一道极其经典的位运算题目
  • 首先,这是一个有很多特殊情况的题:
    • 指数为0时,直接返回1
    • 底为0,但是指数为负数时,发生除0异常,指数为正数时,返回0;
    • 其他情况(使用以下三种方法)
  • 方法1:判断完边界条件后,直接循环,若指数绝对值大小为 n ,那么该方法时间复杂度为 O(n)
  • 方法2 : 利用快速幂的思想,例如 求 3^11, 那么指数 11 的二进制为 1011 ,对于 1011 ,从右到左,每一位分别对应着 1, 2, 4, 8.而 3^11 = 3^(1011) = 3^(1000 + 0010 + 0001)= 3^8 * 3^2 + 3^1。所以我们可以这样看,1011从右到左依次对应着 3,3^2, 3^4, 3^8,这是一种翻倍的关系。
    所以,只要我们对指数的二进制逐位得到每一位数字,看当前位是1还是0,若是1,则累乘进res,并在每一次逐位的过程中完成翻倍,3,3^2, 3^4, 3^8。
    那么如何逐位得到二进制的每一位数字呢? 利用 左移>> 与 & ,&1 可以得到最低位的值,然后 将当前数字 >> 可以得到 下一位值上的数,直至变为0。

    时间复杂度: O(log N)

  • 方法3: 递归。3^11 = 3^5 * 3^5 3 3^10 = 3^5 3^5. 所以首先判断指数的奇偶性

    • 若为奇数:f(base, exp) = f(base, exp > >1) * f(base, exp > >1) * base
    • 若为偶数:f(base, exp) = f(base, exp > >1) * f(base, exp >> 1)

    时间复杂度:O(log N)

经验教训

  • 快速幂的使用
  • 如何利用 >> 与 &1 得到一个数二进制形式的每一位
  • 大于等于1中的任何一个数都可由 1,2,4,8,16,32….组合相加得到,那么由此可知,若想加出奇数,必须使用1,一旦使用1,该奇数的二进制末位一定是 1 。所以可以用 n & 1 != 0 来判断 n 的奇偶性。 注意 ,用 n % 2 != 0来判断奇偶时,后面必须是 != 0,不能是 != 1,因为负数时,比如 -1,余数是 -1,但是仍然是奇数。
  • n >> 1达到除以2的效果,当然,这是针对于正数,负数会带符号右移
  • 计算 2^n ,便是 1 << n,其他可用快速幂求解。
  • 代码必须经常看,这是一道位运算非常经典的题目

代码实现:

  • 方法2:
    public double Power(double base, int exponent) {
        //指数为0
        if (exponent == 0) {
            return 1.0;
        }
        if (base - 0.0 == 0.00001 || base - 0.0 == -0.00001)  {
            //底为0,指数为负数
            if (exponent < 0) {
                throw new RuntimeException("除0异常"); 
            }else{ //底为0,指数为正数
                return 0.0;
            }
        }
        //先按正指数算
        int e = exponent > 0 ? exponent: -exponent;
        double res = 1;
        //快速幂
        while (e != 0) {
            //根据当前位是1还是0决定累乘还是不累乘
            res = (e & 1) != 0 ? res * base : res;
            //翻倍
            base *= base;
            //右移,迭代
            e = e >> 1;
        }
        //根据指数正负,返回
        return exponent > 0 ? res : 1/res;
  }
  • 方法3:
    public double Power(double base, int exponent) {
        if (exponent == 0) {
            return 1.0;
        }
        if (base - 0.0 == 0.00001 || base - 0.0 == -0.00001 )  {
            if (exponent < 0) {
                throw new RuntimeException("除0异常"); 
            }else{
                return 0.0;
            }
        }
        return exponent > 0 ? getPower(base, exponent) : 1/getPower(base, -exponent);
    }

    //传入的e肯定是正数
    public static double getPower(double base, int e) {
        //base case
        if (e == 1) {
            return base;
        }
        //递归计算 f(base,e >> 1)
        double halfPower = getPower(base, e >> 1);
        //根据指数的奇偶计算 f(base,e)
        return (e & 1) != 0 ? base * halfPower * halfPower : halfPower * halfPower;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值