递归树:如何借助树来求解递归算法的时间复杂度?
递归树与时间复杂度分析
递归就是将大问题分解成小问题来求解,将一层一层的分解过程画成图,其实就是一棵树,就是递归树
实战一:分析快排的时间复杂度
用递归树来分析快排的平均情况时间复杂度
每次分区之后,两个分区的大小比例是1:K,取k=9,即每次分区都很不平均,一个分区是另一个分区的9倍,快排过程中,每次分区都要遍历待分区区间的所有数据,所以,每一层分区操作所遍历的数据的个数之和就是n,递归树的高度是h,快排过程遍历的数据个数就是h * n,时间复杂度就是O(h*n)
快排结束的条件就是待排序的小区间大小为1,即叶子节点里的数据规模是1,从根节点n到叶子节点1,递归树中最短的一个路径每次都乘以 ,最长的一个路径每次都乘以 ,所以从根节点到叶子节点的最短路径是 ,最长的路径是 ,当分区大小比例是1:9,快排时间复杂度是
如果K = 99,分区及其不平均,两个分区大小是1:99,时间复杂度是多少呢?
K = 99 ,树的最短路径是log100n,时间复杂度仍然是 ,不管底数多少,快排的时间复杂度就是
实战二:分析斐波那契数列的时间复杂度
int f (int n){
if (n == 1) return 1;
if(n == 2) return 2;
return f(n-1) + f(n-2);
}
这棵递归树的高度是多少呢?f(n)分解成f(n-1)& f(n-2),每次数据规模都是 -1 -2,叶子规模是1 或者2,如果每次都是-1,最长路径是n,如果每次都是-2,最短路径是n/2
每次分解之后的合并操作只需要依次加法运算,从上往下,第一层总时间消耗是1,第二层总时间消耗是2,第三层总时间消耗是 ,……第k层的时间消耗是 ,整个算法的总时间消耗就是每一层时间消耗之和,整个算法的时间复杂度介于 ,和 之间,时间复杂度是指数级的,非常高
实战三:分析全排列的时间复杂度
如何把n个数据的所有排列都找出来?
比如1,2,3三个数据,有6种不同的排列,那么如何通过编程打印一组数据的所有排列呢?可以用递归来实现
我们确定了最后一位数据,就变成了求解剩下n-1个数据的排列,而最后一位数据可以是n个数据中的任意一个,因此它的取值就有n种情况,所以“n个数据的排列”问题就分解成n个“n-1个数据的排列的子问题”
假设数组中存储的是1,2,3……n
f(1,2……n) = {最后一位是1,f(n-1)} + {最后一位是2,f(n-1)} + {最后一位是n,f(n-1)}
//调用方式:
//int[] a = {1,2,3,4} ; printPermutations(a ,4,4);
//k表示要处理的子数组的数据个数
public void printPermutations(int[] data , int n , int k){
if (k == 1){
for (int i = 0 ;i < n ; ++ i){
System.out.print(data[i] + " ");
}
System.out.println();
}
for(int i = 0;i < k ;++i){
int tmp = data[i] ;
data[i] = data[k-1];
data[k-1] = tmp;
printPermutaations(data , n,k-1);
tmp = data[i];
data[i] = data[k-1];
data[k-1] = tmp;
}
}
每一层分解有n次交换操作,第二层有n个节点,每个节点分解需要n-1次交换,所以第二层总的交换次数是n*(n-1),第三层的交换次数是n*(n-1)(n-2),以此类推
一个细胞的生命周期是3个小时,一个小时分裂一次,求n个小时后,容器内有多少细胞?
以为递推公式是f(n) = f(n-1)*2 - f(n-3)
但是这是错的
因为第一个小时的2个细胞有一个已经在第三个小时死掉了,因此第四个小时只会死1个细胞,所以n=4的时候是13
1,2,4,7,13
死掉的细胞数并不是前3个小时新生的细胞和老细胞,因为老细胞在n时刻已经死掉了,此时死掉的细胞数应该是n-3时刻新生的细胞数,所以正确的递推公式是f(n) = 2f(n-1) - f(n-4)
if(n<0) return 0;
if(n == 0) return 1;
if(n ==1) return 2;
if(n ==2) return 4;
if(n == 3) return 7;