5个例子秒懂算法时间复杂度分析

243 阅读5分钟

在计算机科学中,算法的时间复杂度是衡量算法效率的重要指标。时间复杂度描述了算法在输入规模增加时,执行时间如何增长。本文将通过一些常见的算法示例,介绍如何进行时间复杂度分析。

常见的时间复杂度

以下是一些常见的时间复杂度及其含义:

  1. O(1) : 常数时间复杂度,算法的执行时间不随输入规模的变化而变化。
  2. O(log n) : 对数时间复杂度,算法的执行时间随输入规模的对数增长。
  3. O(√n) : 平方根时间复杂度,算法的执行时间随输入规模的平方根增长。
  4. O(n) : 线性时间复杂度,算法的执行时间与输入规模成正比。
  5. O(n log n) : 线性对数时间复杂度,常见于一些高效的排序算法。
  6. O(n^2) : 二次时间复杂度,常见于简单的嵌套循环。
  7. O(n^3) : 三次时间复杂度,常见于三重嵌套循环。
  8. O(2^n) : 指数时间复杂度,常见于一些递归算法。

示例分析

让我们从两个简单的例子开始

def algo_constant(n):
    """Time: O(1)"""
    print(f"do something for {n}")

def algo_linear_0(n):
    """Time: O(n)"""
    for i in range(n):
        print(f"do something for {i}")

这两个示例中,一个是O(1), 一个是O(n),相信你凭直觉就能找到答案,不需要任何过度分析

再看一个没那么无聊的例子,如果迭代的步长不是1


def algo_linear_2(n):
    """Time: O(n)"""

for i in range(0, n, 2):
    print(f"do something for {i}")

凭直觉能看出这个函数迭代的次数为n/2, O(n/2) = O(n),因为两者没有量级的差异,当n极大的时候,因子1/2可以忽略不计,同理当步长为3,4,5...时,算法的时间复杂度都是O(n)我们可以得出结论,线性迭代的步长不影响时间复杂度。

更一般的分析方法

O(n)的算法都比较容易看出来,再来看一个不是O(n)的例子


def alog_logrithemic(n):
    """Time: O(log(n))"""
    i = 1
    while i < n:
        print(f"do something for {i}")
        i *= 2

这个函数时间复杂度为 O(log n)。如何得出这个结论呢?让我们来一步步分析

  1. 定义问题:设迭代次数为k , 时间复杂度可以简化为找出迭代次数kn的关系
  2. 停止条件:k的大小取决于循环停止条件,在这个例子中是 i >= n,可以简化为i = n
  3. 找到迭代变量与k的关系:迭代变量是i,每次迭代中,i 的值都会变为之前的 2 倍,即 i = 1, 2, 4, 8, ..., 可以发现i = 2^k
  4. 求解k:i = 2^k代入停止条件,得到 2^k = n, 因此 k = log2(n)
  5. 得出结论:循环的迭代次数 k 是 log2(n) 的数量级。这意味着算法的时间复杂度是 O(log(n))

以上的分析思路基本就是分析迭代时间复杂度的方法了,让我们用这个方法试试分析另一个例子

def algo_root(n):
    """Time: O(sqrt(n))"""
    i = 1
    while i * i < n:
        print(f"do something for {i}")
        i += 1

该函数的时间复杂度为 O(√n)。分析如下

  1. 定义问题:设迭代次数为k , 时间复杂度可以简化为找出迭代次数kn的关系
  2. 停止条件:k的大小取决于循环停止条件,在这个例子中是 i*i >= n,可以简化为i*i = n
  3. 找到迭代变量与k的关系:迭代变量是i,每次迭代中, i = 1, 2, 3, 4, ..., 可以发现i = k
  4. 求解ki=k代入停止条件,得到 k*k = n, 因此 k = √n
  5. 得出结论:循环的迭代次数 k 是 √n 的数量级。这意味着算法的时间复杂度是 O(√n)

嵌套循环

如果两个循环嵌套会怎样呢?例如这个例子


def algo_nlogn(n):
    """Time: O(n log(n))"""
    for i in range(n):
        j = 1
        while j < n:
            print(f"do something for {i} and {j}")
            j *= 2

在这个函数中,外层循环执行 n 次,内层循环执行 log(n) 次,因此总的时间复杂度为 O(n log n)

总结

K分析法

K分析法是一种系统化的方法,用于分析算法的时间复杂度。其核心思想是通过定义迭代次数 k 来找出算法的时间复杂度。具体步骤如下:

  1. 定义问题:设迭代次数为 k,时间复杂度可以简化为找出迭代次数 k 和输入规模 n 的关系。
  2. 停止条件:确定循环的停止条件,例如某个变量达到特定值。
  3. 找到迭代变量与 k 的关系:分析每次迭代中变量的变化情况。
  4. 求解 k :将迭代变量的关系代入停止条件,用代数方法求解 k
  5. 得出结论:根据 k 的数量级,得出算法的时间复杂度。

通过这几个步骤,我们可以系统地分析出各种复杂度的算法,如 O(log n)O(√n) 等。

嵌套循环乘法规则

嵌套循环乘法规则用于分析含有嵌套循环的算法的时间复杂度。其基本思想是将每层循环的时间复杂度相乘。假设有两个嵌套循环,外层循环的时间复杂度为 O(f(n)),内层循环的时间复杂度为 O(g(n)),那么整个算法的时间复杂度为 O(f(n)g(n))

通过理解和应用K分析法和嵌套循环乘法规则,我们可以快速分析出多层嵌套循环的时间复杂度。