Hello算法2:复杂度分析

Hello算法2:复杂度分析

本文是基于k神的Hello 算法的读书笔记,请支持实体书。
https://www.hello-algo.com/chapter_paperbook/

算法效率

算法效率评估

设计算法时,我们追求以下两个目标:

  • 找出解法
  • 找出最优解

最优解通常包含两个指标:

  • 时间:算法运行的时间
  • 空间:算法占用的内存

评估的方法分为两种:

实际测试和理论评估

实际测试

找一台计算机实际运行代码,比较两个算法的效率。

这样虽然很直观,但是有以下缺陷

  • 难以排除测试环境的干扰:两台计算机配置不同,在上面运行某算法可能会得到不同的结果
  • 完整的测试很耗费资源:某些算法对于少量数据和巨量数据表现差别很大,这就需要设计测试时尽量覆盖所有情况

理论估算

也就是复杂度分析,它描述随着输入数据大小的增加,算法执行的时间和空间的增长趋势。

它客服了实际测试的缺点,体现如下:

  • 无需测试环境,结果适用于各平台
  • 可以体现不同数据量下的效率,特别是针对大数据

迭代和递归

在执行算法时,重复执行某个任务很常见。

迭代

常见的迭代有for循环和while循环

for循环

比如计算1+2+n的值,for循环的时间复杂度取决于n的值,它是线性相关的

while循环

初始化条件和条件更新的步骤独立于循环过程,所以while循环更自由

循环的嵌套

循环中可以嵌套循环,此时复杂度就变成了n的平方

递归

递归是指通过调用函数自身来解决问题,主要包含两个阶段

  1. 递:不断深入的调用自身,通常是不断传入更小的参数,直到达到终止条件
  2. 归:触发终止条件后,程序开始逐层返回
def recur(n: int) -> int:
    """递归"""
    # 终止条件
    if n == 1:
        return 1
    # 递:递归调用
    res = recur(n - 1)
    # 归:返回结果
    return n + res

调用栈

每次调用自身,计算机都会开辟新的内存区域,存储局部变量、调用地址和其他变量,因此

  • 递归通常比迭代更耗费内存空间
  • 递归的时间效率通常更低

递归深度过多,可能会溢出

尾递归

在返回前的最后一步才进行递归调用中调用自身,这种方式某些语言可以被编译器或解释器优化,使其在空间效率上与迭代相当。但比如python就不支持。

def tail_recur(n, res):
    """尾递归"""
    # 终止条件
    if n == 0:
        return res
    # 尾递归调用
    return tail_recur(n - 1, res + n)

递归树

以“斐波那契数列”为例,将会产生如下的递归树

给定一个斐波那契数列0,1,1,2,3,5,8,13,…,求该数列的第n个数字

def fib(n: int) -> int:
    """斐波那契数列:递归"""
    # 终止条件 f(1) = 0, f(2) = 1
    if n == 1 or n == 2:
        return n - 1
    # 递归调用 f(n) = f(n-1) + f(n-2)
    res = fib(n - 1) + fib(n - 2)
    # 返回结果 f(n)
    return res

在这里插入图片描述

时间增长趋势

如下三个算法:

# 算法 A 的时间复杂度:常数阶
def algorithm_A(n: int):
    print(0)
# 算法 B 的时间复杂度:线性阶
def algorithm_B(n: int):
    for _ in range(n):
        print(0)
# 算法 C 的时间复杂度:常数阶
def algorithm_C(n: int):
    for _ in range(1000000):
        print(0)

时间增长趋势如下:

在这里插入图片描述

可以看出时间复杂度有如下特点:

  • 可以有效评估算法效率
  • 时间复杂度的推算更简便
  • 存在一定局限性

计算方式

第一步:统计操作数量,有以下技巧

  1. 忽略常数项
  2. 省略所有系数
  3. 循环嵌套时使用乘法
def algorithm(n: int):
    a = 1      # +0(技巧 1)
    a = a + n  # +0(技巧 1)
    # +n(技巧 2)
    for i in range(5 * n + 1):
        print(0)
    # +n*n(技巧 3)
    for i in range(2 * n):
        for j in range(n + 1):
            print(0)

应用上述方法后,可得操作数量是

在这里插入图片描述

第二步:逐渐判断上界

时间复杂度由 T(n) 中最高阶的项来决定。这是因为在 n 趋于无穷大时,最高阶的项将发挥主导作用,其他项的影响都可以忽略。

常见有如下复杂度:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值