leetcode 50. Pow(x, n)

实现 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/2^{2} = 1/4 = 0.25

提示:

-100.0 < x < 100.0
-2^{31} <= n <= 2^{31}-1
--10^{4}<= x^{n}<= 10^{4}

题目来源于leetcode

这个题目就是给定x和n,求x的n次方。其实这个问题最直接的解法就是采用各种语言提供的库函数,比如Math.pow(x,n)。如果仅仅是调用库函数,那这个问题就没有出现的意义了。下面不使用官方自带的库函数,尝试自己来分析试试。

方法一、一遍循环

其实要求x 的 n次方,最直接的办法就是 n 个 x 相乘,形如下面这样,当然 n 为负数时特殊处理一下就好了

double res = 1.0;
for (int i = 0; i < n; i++) {
       res *= x;
}

这种算法的时间复杂度是O(n)。

方法二、分治法思想

通过公式x^{N} = x^{N/2} *  x^{N/2}x^{n}可以把问题转换为子问题,同时使问题规模变小

递归终止条件,当 N == 0 时,结果就是 1 

但是此处需要注意下,如果 要区分下 N 的奇偶性,奇数情况和偶数情况稍微有点不同

(1)当 N 是 偶数时,如 N = 4,则 N/2 = 2, N/2 + N/2刚好等于N

        递推公式可以转换为  x^{N} = x^{N/2} *  x^{N/2}x^{n}

(2)当 N 时 奇数时,如 N = 5,则 N/2 = 2, N/2 + N/2 = 4,其实时小于N的

        递推公式可以转换为   x^{N} = x^{N/2} *  x^{N/2}  * x^{1}

当然需要对边界进行处理,如果 N 为负数时,比如 N = -2时,x^{N} = \frac{1}{x^{-N}}=(\frac{1}{x})^{-N},最终经过分析,代码如下。部分边界条件在代码中进行注释说明。有个边界需要特殊说明下,如下代码所示

public static void main(String[] args) {
        int value = -2147483648; //-2(31)
        System.out.println(value);
        System.out.println(-value);
    }

输出结果如下所示:

 从上图中可以看出,当value的值表示int的最小值时,-value会越界,所以这里需要特殊处理下。最终的代码如下,结合上面的分析和代码比较好理解了

public double myPow(double x, int n) {
//n 个 0相乘的结果还是0
        if (x == 0) return 0;

        //非0的0次方都是1
        if (n == 0) return 1;

        //此处需要把n赋值给精度更大的N,否则当n = -2147483648时,-n会出现越界的情况
        long N = n;
        if (N < 0) {
            N = -N;
            x = 1 / x;
        }

        return myPow1(x, N);
    }

    public double myPow1(double x, long N) {
        if (N == 0) return 1.0;
        double value = myPow1(x, N / 2);
        //偶数
        if (N % 2 == 0) {
            return value * value;
        } else {
            return value * value * x;
        }
    }
}

这种算法的时间复杂度是O(lgn),因为求 x^{N} 会转换为求 x^{N/2} 、x^{\frac{N}{4}}x^{\frac{N}{8}}...1、问题的规模会不断的变小,这种算法的空间复杂度也是O(lgn),递归需要的栈。

方法三、快速幂

下面分析下这种方法的思路,以 N 为正整数进行说明,负整数的情况是类似的,正整数 N 的二进制表示为如下所示。

N (十进制)= bm bm-1.... b3  b2  b1  b0(二进制表示)其中 b_{i} 表示转换为二进制后的位,b_{i} 的值域为 {0,1 }。

则 N = b0 * 2^{0}  + b_{1} * 2^{1}b_{2} * 2^{2} + .... + b_{m} * 2^{m}

则 X^{N} = X^{b_{0}*2^{0} + b_{1}*2^{1} + b_{2}*2^{2}+...+ b_{m}*2^{m}}

X^{N} = X^{b_{0}*2^{0}} * X^{b_{1}*2^{1}} * X^{b_{2}*2^{2}} * ... * X^{b_{m}*2^{m}}

则 X^{N}=X^{2^{0}*b_{0}} * X^{2^{1}*b_{1}} * X^{2^{2}*b_{2}} *...* X^{2^{m}*b_{m}}

我们知道 b_{i} 的值域是{0 ,1},当 b_{i} 的值是0时,任何非 0 的 0 次方都是0,当 b_{i} 的值是1时,任何数乘以 1 都等于其本身。那剥离开 b_{i} 先不管,看看上面表达式 可以转换为:

X^{2^{0}} * X^{2^{1}} * X^{2^{2}}*... * X^{2^{m}},不就是等于前面一个 乘以 X ?

最终代码如下:

public double myPow(double x, int n) {
//n 个 0相乘的结果还是0
        if (x == 0) return 0;

        //非0的0次方都是1
        if (n == 0) return 1;

        //此处需要把n赋值给精度更大的N,否则当n = -2147483648时,-n会出现越界的情况
        long N = n;
        if (N < 0) {
            N = -N;
            x = 1 / x;
        }

        double res = 1;
        while (N > 0) {
            //判断 N 的二进制的最后一位是否为1,根据前面的表述,如果 bi 是 1,则需要计算进入结果
            //如果bi 是 0,非 0 的0 次方是1,所以不需要计入结果
            if ((N & 1) == 1) {
                res = res * x;
            }

            //接下来需要处理下一项,所以需要把x = x * x
            x = x * x;
            //需要处理下一个位,所以需要把 N 右移一位
            N = (N >> 1);
        }
        return res;
    }
}

时间复杂度是O(lgn),每一次把N右移1位(等同于除以2),直到0。空间复杂度是O(1)

你可能感兴趣的:(算法,leetcode)