数据结构与算法之美 | 学习笔记23 —— 递归树

一、递归树基础

借助递归树分析递归算法的时间复杂度。把待解决的问题一层层分解画成图,就形成了一颗递归树。数据结构与算法之美 | 学习笔记23 —— 递归树_第1张图片
将归并排序算法画成递归树,是一颗满二叉树:
数据结构与算法之美 | 学习笔记23 —— 递归树_第2张图片

二、递归树应用

1. 分析快速排序的时间复杂度

例如,对于快速排序,假设平均情况下,每次分区的大小比例为1:k,当k=9时,如果用递推公式,则为 T ( n ) = T ( n 10 ) + T ( 9 n 10 ) + n T(n)=T(\frac {n}{10} )+T(\frac {9n}{10})+n T(n)=T(10n)+T(109n)+n,如果用递归树,数据结构与算法之美 | 学习笔记23 —— 递归树_第3张图片
整个遍历的时间复杂度为 O ( h ∗ n ) O(h*n) O(hn),因为每次分区不是均匀一分为二,这个递归树不是满二叉树。递归树中最短的路径每次都乘以 1 10 \frac {1}{10} 101, 最长的每次乘 9 10 \frac {9}{10} 109,因此整个根节点到叶子结点的遍历个数:
数据结构与算法之美 | 学习笔记23 —— 递归树_第4张图片
无论如何,快排的平均时间复杂度都为 O ( n l o g n ) O(nlogn) O(nlogn).

2. 分析斐波那契数列的时间复杂度

举例递归中依次跨一个或两个台阶的问题:

int f(int n) {
     
  if (n == 1) return 1;
  if (n == 2) return 2;
  return f(n-1) + f(n-2);
}

用递归树进行分析:
数据结构与算法之美 | 学习笔记23 —— 递归树_第5张图片
如果每次都是-1的走法,那么最长路径为n,如果每次都是-2的走法,那么最短路径大约为 n 2 \frac n2 2n. 每次分解之后的合并操作只需要一次加法运算,时间消耗记为1。对于最长路径n,时间消耗总和为:
1
对于最短路径 n 2 \frac n2 2n,时间消耗总和为:
1
因此我们大概知道这个算法时间复杂度是指数级的,非常高。

3. 分析全排列的时间复杂度

编程打印一组数据的全排列,例如[1,2,3]:

1, 2, 3
1, 3, 2
2, 1, 3
2, 3, 1
3, 1, 2
3, 2, 1

对于n个数的全排列,可以分解为:
f(1,2,...n) = {最后一位是1, f(n-1)} + {最后一位是2, f(n-1)} +...+{最后一位是n, f(n-1)}

// 调用方式:
// int[]a = 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;

    printPermutations(data, n, k - 1);

    tmp = data[i];
    data[i] = data[k-1];
    data[k-1] = tmp;
  }
}

画出递归树:
数据结构与算法之美 | 学习笔记23 —— 递归树_第6张图片
第一层有n次交换操作,依次类推,总共的交换次数为n + n*(n-1) + n*(n-1)*(n-2) +... + n*(n-1)*(n-2)*...*2*1,全排列的递归算法时间复杂度大于 O ( n ! ) O(n!) O(n!), 小于 O ( n ∗ n ! ) O(n*n!) O(nn!)

你可能感兴趣的:(数据结构,算法,数据结构,快速排序)