算法之时间复杂度和空间复杂度

一,概述

  算法(Algorithm)是指用来操作数据、解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的。数据结构和算法本生解决的就是“快”和“省”的问题,那就是如何让代码跑得快,还能节省存储空间。好比打造一台法拉利,不仅跑得快还省油,拥有好的算法与数据结构,程序跑得快,还省内存并且长时间运行也不会出故障,就像跑车长时间运行车子也不会出现异常震动,同时还快。

  那么我们应该如何去衡量不同算法之间的优劣呢?

  主要还是从算法所占用的「时间」和「空间」两个维度去考量。

  • 时间维度:是指执行当前算法所消耗的时间,我们通常用「时间复杂度」来描述。

    一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。

    前面提到的时间频度T(n)中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律,为此我们引入时间复杂度的概念。一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数,记作T(n)=O(f(n)),它称为算法的渐进时间复杂度,简称时间复杂度。

  • 空间维度:是指执行当前算法需要占用多少内存空间,我们通常用「空间复杂度」来描述。
  时间复杂度和空间复杂度可以帮助我们根据具体的平台选择合适的算法,要学会以空间换时间或以时间换空间的设计思想,如在单片机等一般是内存空间比较紧张,在追求最优算法时应该可以适当以时间来换空间进行设计,当然在大内存设备上可以选择以空间换时间的设计思想来设计最优算法,所以,时间和空间复杂度可在一定的限制条件下作为判断某个算法或代码块运行快慢的判断方式。

二,大O表示法

  像前面用O( )来体现算法时间复杂度的记法,我们称之为大O表示法。算法复杂度可以从最理想情况、平均情况和最坏情况三个角度来评估,由于平均情况大多和最坏情况持平,而且评估最坏情况也可以避免后顾之忧,因此一般情况下,我们设计算法时都要直接估算最坏情况的复杂度。

  大O表示法O(f(n)中的f(n)的值可以为1、n、logn、n²等,因此我们可以将O(1)、O(n)、O(logn)、O(n²)分别可以称为常数阶、线性阶、对数阶和平方阶,那么如何推导出f(n)的值呢?我们接着来看推导大O阶的方法。

  • 推导大O阶

    推导大O阶,我们可以按照如下的规则来进行推导,得到的结果就是大O表示法:

    1.用常数1来取代运行时间中所有加法常数。

    2.修改后的运行次数函数中,只保留最高阶项

    3.如果最高阶项存在且不是1,则去除与这个项相乘的常数。

三,时间复杂度分析

  • 常见的时间复杂度量级

    从小到大依次是:

    常数阶(O(1)) < 对数阶(O(logn)) < 线性阶(n) < 线性对数阶(O(nlogn)) < 平方阶(O(n^2)) < 立方阶(O(n^3)) < 阶乘阶(O(n!)) < n次方阶(O(n^n))

  • 常数阶

    先举了例子,如下所示。

    ///语句执行1次
    let n = 10
    ///语句执行1次
    let sum = (1 + n)*n/2
    ///语句执行1次
    print("The sum is :\(sum)")
    ///总共执行次数:3

    上面算法的运行的次数的函数为f(n)= (1 + 1 + 1),根据推导大O阶的规则1(用常数1来取代运行时间中所有加法常数),我们需要将常数3改为1,则这个算法的时间复杂度为O(1)。如果sum = (1+n)*n/2这条语句再执行10遍,因为这与问题大小n的值并没有关系,所以这个算法的时间复杂度仍旧是O(1),我们可以称之为常数阶。

  • 线性阶

    线性阶主要要分析循环结构的运行情况,如下所示。

    ///语句执行1次
    var i = 0
    ///语句执行n次
    while i < n {
        ///语句执行n次
        i+=1
        ///语句执行n次
        print("Current i is \(i)")
    }
    ///总执行数:3n+1 = 1 + n + n + n

    上面算法的运行的次数的函数为 f(n)= 3n + 1次,根据规则 2(修改后的运行次数函数中,只保留最高阶项) 规则3(如果最高阶项存在且不是1,则去除与这个项相乘的常数。),因此该算法的时间复杂度是O(n)。

  • 对数阶

    接着看如下代码:

    let number = 1; // 语句执行一次
    while (number < n) { // 语句执行log(n)次
      number *= 2; // 语句执行log(n)次
    }
    ///总执行数:2log(n)+1 = 1 + log(n) + log(n)

    上面算法的运行的次数的函数为 f(n)= (2log(n) + 1)次,number每次都放大两倍,我们假设这个循环体执行了m次,那么2^m = nm = log(n),所以整段代码执行次数为1 + 2*logn,则f(n) = log(n),时间复杂度为O(log(n))。

  • 平方阶

    下面的代码是循环嵌套:

    /// 语句执行n次
    for (let i = 0; i < n; i++) {
        ///语句执行n^2次
        for (let j = 0; j < n; j++) {
            ///语句执行n^2
            print('I am here!')
        }
    }

    上面算法的运行的次数的函数为 f(n)= (2*n^2 + n)次, 内层循环的时间复杂度在讲到线性阶时就已经得知是O(n),现在经过外层循环n次,那么这段算法的时间复杂度则为O(n²)。

四,空复杂度分析

  时间复杂度的全称是渐进时间复杂度表示算法的执行时间与数据规模之间的增长关系。类比一下,空间复杂度全称就是渐进空间复杂度表示算法的存储空间与数据规模之间的增长关系

void print(int n) {
  int i = 0;
  int[] a = new int[n];
  for (i; i <n; ++i) {
    a[i] = i * i;
  }
}

  跟时间复杂度分析一样,我们可以看到,第 2 行代码中,我们申请了一个空间存储变量 i,但是它是常量阶的,跟数据规模 n 没有关系,所以我们可以忽略。第 3 行申请了一个大小为 n 的 int 类型数组,除此之外,剩下的代码都没有占用更多的空间,所以整段代码的空间复杂度就是 O(n)。

  打个不恰当的比喻,就像我们的手机现在工艺越来越好,手机也越来越薄。占用体积越来越小。也就是用更好的模具设计放置零件,而模具就像是空间复杂度更小的体积容纳更多的原件。

  我们常见的空间复杂度就是 O(1)、O(n)、O(n2 ),像 O(logn)、O(nlogn) 这样的对数阶复杂度平时都用不到。而且,空间复杂度分析比时间复杂度分析要简单很多。所以,对于空间复杂度,掌握刚我说的这些内容已经足够了。

  • 空间复杂度 O(1)

    如果算法执行所需要的临时空间不随着某个变量n的大小而变化,即此算法空间复杂度为一个常量,可表示为 O(1)。

    int i = 1;
    int j = 2;
    ++i;
    j++;
    int m = i + j;

    代码中的 i、j、m 所分配的空间都不随着处理数据量变化,因此它的空间复杂度 S(n) = O(1)。

  • 空间复杂度 O(n)

    int[] m = new int[n]
    for(i=1; i <= n; ++i) {
       j = i;
       j++;
    }

    这段代码中,第一行new了一个数组出来,这个数据占用的大小为n,后面虽然有循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,即 S(n) = O(n)。

posted on   梁飞宇  阅读(325)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示