利用递归树分析归并排序复杂度
每一层归并操作消耗的时间总和是一样的,跟要排序的数据规模有关。把每一层归并操作消耗的时间记作 n。加上归并排序递归树是一棵满二叉树。满二叉树的高度大约是 log2n,所以,归并排序递归实现的时间复杂度就是 O(nlogn)。
利用递归树分析快速排序复杂度
快速排序在最好情况下,每次分区都能一分为二,这个时候用递推公式 T(n)=2T(2/n)+n,很容易就能推导出时间复杂度是 O(nlogn)。
假设两个分区的大小比例为 1:k。当 k=9 时,如果用递推公式的方法来求解时间复杂度的话,递推公式就写成 T(n)=T(n/10)+T(9n/10)+n
快速排序的过程中,每次分区都要遍历待分区区间的所有数据,所以,每一层分区操作所遍历的数据的个数之和就是 n。因为每次分区并不是均匀地一分为二,所以递归树并不是满二叉树。
快速排序结束的条件就是待排序的小区间,大小为 1,也就是说叶子节点里的数据规模是 1。从根节点 n 到叶子节点 1,递归树中最短的一个路径每次都乘以1/10,最长的一个路径每次都乘以9/10。通过计算,我们可以得到,从根节点到叶子节点的最短路径是 log10(n)【一直走分区一直为1/10的区间】,最长的路径是 log(10/9)n【一直走分区为9/10的区间】
所以,遍历数据的个数总和就介于 nlog10n 和 nlog(10/9)n 之间。根据复杂度的大 O 表示法,对数复杂度的底数不管是多少,我们统一写成 logn,所以,当分区大小比例是 1:9 时,快速排序的时间复杂度仍然是 O(nlogn)。
分析斐波那契数列的时间复杂度
f(n) 分解为 f(n−1) 和 f(n−2),每次数据规模都是 −1 或者 −2,叶子节点的数据规模是 1 或者 2。所以,从根节点走到叶子节点,每条路径是长短不一的。如果每次都是 −1,那最长路径大约就是 n;如果每次都是 −2,那最短路径大约就是 n/2;
每次分解之后的合并操作只需要一次加法运算,我们把这次加法运算的时间消耗记作 1。所以,从上往下,第一层的总时间消耗是 1,第二层的总时间消耗是 2,第三层的总时间消耗就是 2^2。依次类推,第 k 层的时间消耗就是 2^(k−1),那整个算法的总的时间消耗就是每一层时间消耗之和。
- 如果路径长度都为 n,那这个总和就是 2n−1。
- 如果路径长度都是 n/2,那整个算法的总的时间消耗就是 2^(n/2)−1;
分析全排列的时间复杂度
递归公式
假设数组中存储的是1,2, 3...n。
f(1,2,...n) = {最后一位是1, f(n-1)} + {最后一位是2, f(n-1)} +...+{最后一位是n, f(n-1)}。
代码
function printPermutations( data,n,k) {
if (k == 1) {
for (int i = 0; i < n; ++i) {
console.log(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;
printPermutations(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 次交换,所以第三层总的交换次数是 n∗(n−1)∗(n−2)。以此类推,第 k 层总的交换次数就是 n∗(n−1)∗(n−2)∗...∗(n−k+1)。最后一层的交换次数就是 n∗(n−1)∗(n−2)∗...∗2∗1。每一层的交换次数之和就是总的交换次数。
n + n*(n-1) + n*(n-1)*(n-2) +... + n*(n-1)*(n-2)*...*2*1
这个公式的求和比较复杂,我们看最后一个数,n∗(n−1)∗(n−2)∗...∗2∗1 等于 n!,而前面的 n−1 个数都小于最后一个数,所以,总和肯定小于 n∗n!,也就是说,全排列的递归算法的时间复杂度大于 O(n!),小于 O(n∗n!),虽然没法知道非常精确的时间复杂度,但是这样一个范围表明全排列的时间复杂度是非常高的。
思考
1 个细胞的生命周期是 3 小时,1 小时分裂一次。求 n 小时后,容器内有多少细胞?
- f(n)=2*f(n-1)-f(n-4)。一次乘法和一次减法一起看作一次基本操作消耗,那么情况和斐波那契数列很像。最高的树应该有n层,最短的是n/4层,每层操作数都是指数增长。那么时间复杂度应该是在O(2^n)量级的。