这个实现很简单,就是实现的时候注意细节就可以了。

傻白甜做法

  • 乘方n的取值分成三类情况,大于0,等于0,小于0

  • 逐层递归

漏洞

  • 漏洞一:时间O(n),当n很大时,慢到爆炸

  • 漏洞二:未考虑边界条件n=Integer.MIN_VALUE,这个时候-n赋不进n

    public double myPow1(double x, int n) {
        if (n > 0)
            return myPow1(x, n);
        else if (n == 0)
            return 1;
        else
            return 1 / myPow1(x, -n);
    }

考虑边界

问题:最小值 n = -2147483648,最大值 n' = -n = 2147483648取不到
解决办法:令 n' = n+1 = -2147483647 。求xn等价于求 xn'*x-1

    public double myPow2(double x, int n) {
        if (n > 0)
            return positivePow2(x, n);
        else if (n == 0)
            return 1;
        else if (n != Integer.MIN_VALUE)
            return 1 / positivePow2(x, -n);
        else
            return 1 / positivePow2(x, -(n + 1)) / x;
    }

    private double positivePow2(double x, int n) {
        if (n == 1)
            return x;
        double half = positivePow2(x, n / 2);
        if (n % 2 == 0) {
            return half * half;
        } else {
            return x * half * half;
        }
    }

精简代码

算法想法是把 负的幂次方 先写成 正的幂次方, 最后再取倒数。
上述实现的时候特意另外写了一个辅助函数,其实没有必要,完全可以写在一起,调用自身即可。
当n<0时, 不再最后取倒数,而是每次x取成1/x即可调用自身。
对于递归的认识还是不够深入。

    double myPow(double x, int n) {
        if (n < 0)
            return 1 / x * myPow(1 / x, -(n + 1));
        if (n == 0)
            return 1;
        double half=myPow(x, n / 2);
        if (n % 2 == 0)
            return half * half;
        else
            return x * half * half;
    }

下面这个是人家写的,在x平方的时候仍旧调用了自己,贯彻了递归的想法。
我思考的时候觉得下面和上面的代码应该本质的计算量是一致的,但不知道多一次调用会对效率影响多少。

    double myPow(double x, int n) {
        if (n < 0)
            return 1 / x * myPow(1 / x, -(n + 1));
        if (n == 0)
            return 1;
        if (n == 2)
            return x * x;
        if (n % 2 == 0)
            return myPow(myPow(x, n / 2), 2);
        else
            return x * myPow(myPow(x, n / 2), 2);
    }

最终版

剑指offer有讲解,具体可看P90页。

  • 书中考虑了两个问题

    • 指数为负

    • 判断运算是否有效。有时输入数字的乘方本身是无效的,比如0-2就是无效的(处理方式就是做了个boolean flag来记录有效还是无效)

  • 细节问题

    • 除以2的写法。n/2 可以写作 n>>1

      • 注意是1而不是2,我总是想着是2进制,除以2,所以要右移2,其实是右移1!!!!

    • 判断奇偶。 n % 2 == 0 可以写作 (n & 1) == 0

      • 括号不能漏,运算的先后性

      • 位运算

核心代码如下

    double myPow(double x, int n) {
        if (n < 0)
            return 1 / x * myPow(1 / x, -(n + 1));
        else if (n == 0)
            return 1;
        else if (n == 1) //这个条件写不写都没关系,写了话返回更早一步
            return x;

        double half = myPow(x, n >> 1);
        half *= half;

        if ((n & 1) == 1)
            half *= x;
        return half;
    }

非递归实现

写是写了,但是多用了一个Stack s来记录每次分的份数。比如
19 - 9 - 4 - 2
每次有奇数就说明少乘了一份,所以要记录下来。
切分的时候n>=2的话要继续分,n==1的时候直接返回就可以了。

暂时没想到什么更好的办法。

    double myPow3(double x, int n) {
        if (n == 0)
            return 1;
        if (n < 0)
            return myPow3(1 / x, -(n + 1)) / x;

        Stack<Integer> s = new Stack<Integer>();

        for (; n != 1; n /= 2)
            s.push(n);

        double t = x;
        
        //while (s.peek()!=null) { 出错
        while (!s.isEmpty()) {
            t *= t;
            if (s.pop() % 2 != 0)
                t *= x;
        }
        return t;
    }

lindsay_bubble
26 声望11 粉丝

引用和评论

0 条评论