递归行为时间复杂度的估算:
示例: 求一个数组的最大值 arr[]={3,5,6,2,8,3}
分别求左侧最大值,右侧最大值,最后返回两侧最大值比较后的最大值
int doGetMaxnum(int *arr, int l, int r)
{
if(l == r)
return arr[l];
int mid = l + ( (r - l)>>1);
int leftMax = doGetMaxnum(arr, l, mid);
int rightMax = doGetMaxnum(arr, mid+1, r);
return fmax(leftMax, rightMax);
}
int getMaxnum(int *arr, int len)
{
int l = 0;
int r = len - 1;
return doGetMaxnum(arr, l, r);
}
细化一下递归图(其实就是利用系统栈进行多叉树的遍历)
那么对于这类递归行为,怎样去估算其时间复杂度呢?
master 公式可以用来处理这一系列
T(N) = a * T(N / b) + O(N ^ d)
1. log(b, a) > d -> O(N ^ log(b, a))
2. log(b, a) = d -> O(N ^ d * logN)
3. log(b, a) < d -> O(N ^ d)
T(N):母问题的数据量是 N 级别的
T(N/b):每一次的子问题的规模都是 N / b 级别的,等量规模
a:子问题被调用的次数
O(N ^ d):除去子问题调用之外剩下过程的时间复杂度
现在来看示例的时间复杂度:
母问题:
int doGetMaxnum(int *arr, int l, int r)
l ~ r 上一共有 N 个数
子问题:
int leftMax = doGetMaxnum(arr, l, mid);
int rightMax = doGetMaxnum(arr, mid+1, r);规模是 N / 2
a:
调用了 2 次 等量的子问题
除去子问题外,剩下的内容:
if(l == r) // 比较
int mid = l + ( (r - l)>>1); // 计算
fmax(leftMax, rightMax); // 比较所以时间复杂度是 O(1)
那么我们的 master 公式:
T(N) = 2 * T(N / 2) + O(1)
其中 a = 2, b = 2, d = 0,应用
1. log(b, a) > d -> O(N ^ log(b, a))
log(2,2) > 0 -> O(N ^ log(2, 2)) -> O(N^1) -> O(N)
再扩展下这个示例的推演:
不进行二分之一分段
1. 左侧 2/3 + 右侧 2/3,即使有重复区域,也不影响计算结果
子过程的规模是 2/3 * N ,即 T(N / (3/2) )
2. 左侧 1/3 + 中间 1/3 + 右侧 1/3
子过程的规模 1/3 * N, 即 T(N / 3)
3. 左侧 1/3 + 右侧 2 /3
子过程的规模 = T( N /3 ) + T(N / (3/2) )
不是等量的子过程,不符合 master 公式
4. 左侧 1/3 + 中间 1/3 + 右侧 1/3 完成后,再全部扫描一次数组
T(N) = 3 * T(N /3) + O(N)
一定记住子问题是等量的才行!