从数据规模变化的角度来思考:
-
T(N)=T(N/2)+O(1)
如果每次递归使问题的规模减半,而其他操作都是常数时间, 则T(N)=O(logN)。比如二分法查找,就是很棒的例子。 -
T(N)=T(N-1)+O(1)
若每次递归使用问题的规模减1,而其他操作是常数时间,则T(N)=O(N)。比如我前几天写的链表翻转(递归实现,是一般递归不是尾递归) -
T(N) = T(N/2)+O(N)
若每次递归使问题的规模减半,而其他操作是线性时间,则T(N)=O(NlogN)。很典型就是快排【注1】和归并。
注1:快排的平均时间复杂度是nlogn,因为一个快排树的高度被拆成logN,每层的问题被拆成x个,每个问题的数据规模是N/x,所以每层空间复杂度还是N,高度乘以每层就是NlogN。但是有一种极端情况:比如向深层递归时候每次左边只有1个元素排序,右边有n-1个元素排序,这样递归树就是高度为N,时间复杂度就是N×N了。关键还是看递归树的高度如何(问题规模的拆解如何)。
T(N) = 2T(N/2) + N
class Qs(object):
def sort(self, ls, begin, end):
# 时间复杂度N*O(1) = N
if begin >= end: return
landmark = ls[begin]; r = end; l = begin
while l < r:
while ls[r] >= landmark and r > l:
r -= 1
ls[l] = ls[r]
while ls[l] < landmark and l < r:
l += 1
ls[r] = ls[l]
ls[r] = landmark
# 2 * T(N/2)时间复杂度
self.sort(ls, begin, r-1)
self.sort(ls, r+1, end)