今天学习的是递归函数时间复杂度的简单学习

从求x的n次方这道题的角度了解时间复杂度和递归函数

1.首先是求x的n次方的最一般解法 (for循环解法)

这种解法最容易想到

循环了n次 每次仅有一次运算 它的时间复杂度O(n)

// 使用循环 时间复杂度
	public static int function0(int x, int n){
        int result = 1;
        for (int i = 1; i <= n; i ++){
            result *= x;
        }
        return result;
    }

2.然后是求x的n次方的递归解法(递归解法)

使用递归法实现,它的时间复杂度又是多少呢?

该递归方法中 递归的次数 为n次 每次递归的操作1次

根据递归算法的时间复杂度 = 递归的次数 * 每次递归中的操作次数

可得它的时间复杂度也是O(1)

/**
     * 递归写法
     * 递归算法的时间复杂度 = 递归的次数 * 每次递归中的操作次数
     * 时间复杂度   o(n)         n             1
     */
    public static int function1(int x, int n){
        if (n <= 0){
            return 0;
        }
        if (n == 1){
            return x;
        }
        return function1(x, n-1) * x;
    }

总结:这次递归并没有使得算法的时间复杂度降低

3.那么换一种递归方法(第二种递归解法)

使用第二种递归解法

运用 二分法 将 n 次方 变成 两个 n / 2 次相乘

/**
     * 优化递归算法
     * 运用 二分法 将 n 次方 变成 两个 n / 2 次相乘 进行优化
     *
     * 时间复杂度依旧是    o(n)
     */
public static int function2(int x, int n){
        if (n == 0){
            return 0;
        }
        if (n == 1){
            return x;
        }
        if (n % 2 == 1){
            return function2(x, n / 2) * function2(x, n / 2) * n;
        }
        return function2(x, n / 2) * function2(x, n / 2);
    }

这种方法的时间复杂度仍为O(n)

具体看《代码随想录》中作者的解释

我们来分析一下,首先看递归了多少次呢,可以把递归抽象出一棵满二叉树。

可以用一棵满二叉树来表示(为了方便表示,选择n为偶数16)。

java一个数的n次方用循环 javax的n次方_递归

 

当前这棵二叉树就是求x的n次方,n为16的情况,n为16的时候,进行了多少次乘法运算呢?

这棵树上每一个节点就代表着一次递归并进行了一次相乘操作,所以进行了多少次递归的话,就是看这棵树上有多少个节点。

熟悉二叉树话应该知道如何求满二叉树节点数量,这棵满二叉树的节点数量就是`2^3 + 2^2 + 2^1 + 2^0 = 15`,可以发现:**这其实是等比数列的求和公式,这个结论在二叉树相关的面试题里也经常出现**。

这么如果是求x的n次方,这个递归树有多少个节点呢,如下图所示:(m为深度,从0开始)

java一个数的n次方用循环 javax的n次方_算法_02

 时间复杂度忽略掉常数项-1之后,这个递归算法的时间复杂度依然是O(n)

4.优化上一个递归方法(第二种递归解法优化)

仅仅将function3(x, n / 2)抽取出来,减少递归中操作的次数

就可以真正意义上的降低时间复杂度

该方法的时间复杂度就是O(logn)

/**
     * 再优化递归函数
     * 每次递归仅有一个递归调用
     * 递归算法的时间复杂度 = 递归的次数(也就是二叉树的深度) * 每次递归中的操作次数
     *    o(logn)            logn                           1
     *
     */
    public static int function3(int x, int n){
        if (n == 0){
            return 0;
        }
        if (n == 1){
            return x;
        }
        int t = function3(x, n / 2);
        if (n % 2 == 1){
            return t * t * n;
        }
        return t * t;
    }

         根据以上不断优化的四种不同情况,同一程序的时间复杂度只在最后一次才达到理想中的O(logn),这也给了我们优化算法的集中不同思路与启示,并不仅仅是优化了方法就一定会降低代码的时间复杂度,更多的还需要考虑程序运行的真实情况等等