【题解】剑指 Offer 16. 数值的整数次方

91 阅读2分钟

题目

1663084454(1).png

难度

力扣难度中等,实际难度中等

分类

快速幂|二分法

思路

二分法

这道题,最差的情况就是单循环,x * x * x....... 乘n遍,但是面试算法题就是最简单的方法一定会GG。 目前的时间复杂度是n,进一步优化一定是logn 所以第一时间想到的就是递归二分法,把x相乘的步骤对半砍

  1. x^16,
  2. 砍成x^8 * x^8,
  3. 砍成x^4 * x^4 * x^8
  4. 砍成x^2 * x^2 * x^4 * x^8
  5. 砍成x * x * x^2 * x^4 * x^8 每一轮 只需要算一次x*x=x^2x^2 * x^2 = x^4x^4 * x^4 = x^8x^8 * x^8 = x^16结果就出来了

这个算法如果是奇数的话需要再*x 如果是负数边界值一定要先/2再取反,因为负数边界值比整数大一,所以直接取反翻不过来

二进制快速幂

这个和上面那个时间复杂度一样但是在计算机层面的计算会更快

image.png

大概的意思就是把n拆成二进制,然后根据二进制的1和0决定要不要把x^(2^(1,2,3,4...))乘起来,换句话说就是x^1,x^2,x^4,x^8,x^16.....

比如n = 11 二进制是1101 那么相当于 res = x^1 * x^4 * x^8 只把1,4,8的三项乘起来就是x^11

1663086154(1).png

  1. 直接做位运算可以把n看成二进制不需要进行转换
  2. n&1表示当前位是否是1
  3. 这里的n>>1表示计算下一位

注意

需要注意n可能为负数需要对x做一次 x = 1/x处理

代码

递归实现的二分法

class Solution {
    public double myPow(double x, int n) {
        if (n == 0) {
            return 1;
        }
        if (n > 0) {
            double temp = myPow(x, n / 2);
            if (n % 2 == 0) {
                return temp * temp;
            } else {
                return temp * temp * x;
            }
        } else {
            double temp = myPow(1 / x, -(n / 2));
            if (n % 2 == 0) {
                return temp * temp;
            } else {
                return temp * temp * 1/ x;

            }
        }
    }
}

二进制快速幂

class Solution {
    public double myPow(double x, int n) {
        if(x == 0) return 0;
        long b = n;
        double res = 1.0;
        if(b < 0) {
            x = 1 / x;
            b = -b;
        }
        while(b > 0) {
            if((b & 1) == 1) res *= x;
            x *= x;
            b >>= 1;
        }
        return res;
    }
}