数据结构之时间复杂度

时间复杂度是评估算法运行效率的重要指标,它通过数学函数描述算法执行次数与输入大小的关系。文章介绍了时间复杂度的概念,如T(n) = O(f(n)),并举例说明了不同函数表达式对应的时间复杂度,包括线性、对数、多项式和指数级的时间复杂度,并探讨了在不同输入规模下,不同时间复杂度算法的运行速度对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:为什么会出现时间复杂度这个概?

        我们在编写代码的时候为了完成一个函数功能,可能用到不同的处理方式:顺序多次执行、使用for循环执行、使用递归调用等等。当循环次数取值范围不同时,各种算法处理所耗费的时间也不一样,不同的算法所执行的次数通过分析可以用数学函数表达。

        时间复杂度概念:我们把 算法需要执行的运算次数 用 输入大小n 的函数 表示,即 T(n) 。此时为了 估算算法需要的运行时间 和 简化算法分析,我们引入时间复杂度的概念。

        时间复杂度定义:存在常数 c 和函数 f(N),使得当 N >= c 时 T(N) <= f(N),表示为 T(n) = O(f(n)) 。

实际应用中带来的优势

        如下图:假设现在有两个算法均可以实现一个功能,其运算次数n的函数表达分别为T(n) = n + 2与T(n) = n^2。可以这样理解,同一个功能,一个人只用了一层for循环执行N次,再多运行两次;另一个人用了for循环嵌套for完成,所以执行次数为n^2。

       

         当 N >= 2 的时候,f(n) = n^2 总是大于 T(n) = n + 2 的,于是我们说 f(n) 的增长速度是大于或者等于 T(n) 的,也说 f(n) 是 T(n) 的上界,可以表示为 T(n) = O(f(n))。

        因为f(n) 的增长速度是大于或者等于 T(n) 的,即T(n) = O(f(n)),所以我们可以用 f(n) 的增长速度来度量 T(n) 的增长速度,所以我们说这个算法的时间复杂度是 O(f(n))。

        算法的时间复杂度,用来度量算法的运行时间,记作: T(n) = O(f(n))。它表示随着 输入大小n 的增大,算法执行需要的时间的增长速度可以用 f(n) 来描述。

总结:通过对算法进行数学函数分析,可以得到N在某个范围内,使用适合的算法,而N的值使我们在函数传参时,写入的,所以我们事先设计2套算法,当N<2时用算法1,N>时用算法2,这样提高的运行效率。

实际代码中常出现的函数表达式

场景1:T(n) = 3n,执行次数是线性的。

        该函数只有一层for循环,且次数i线性增长,没有嵌套循环语句,所以是线性函数(这里执行一次打印函数为一个循环单位,for包含3个,所以为 3n)

void aFunc(int n) {
    for(int i = 0; i < n; i++) {         // 循环次数为 n
        printf("Hello, World!\n");      // 循环体时间复杂度为 O(1)
    }
}

场景2:T(n) = logn,执行次数是对数。

        该函数变量i指数增长,_2{i}<n,两边去对数,执行次数i =\log _{2}n ,即 T(n) =\log _{2}n,可见时间复杂度为 O(\log _{2}n),即表示对数函数表达式,统一称为 O(log n),不用带上底数2。


void eat2(int n){
   for(int i=1; i<n; i*=2){
       System.out.println("等待一天");
       System.out.println("等待一天");
       System.out.println("等待一天");
       System.out.println("等待一天");
       System.out.println("吃一半面包");
   }

场景4:T(n) =  n^2 / 2 + n / 2,执行次数是一个多项式。

        当 i = 0 时,内循环执行 n 次运算,当 i = 1 时,内循环执行 n - 1 次运算……当 i = n - 1 时,内循环执行 1 次运算。
所以,执行次数 T(n) = n + (n - 1) + (n - 2)……+ 1 = n(n + 1) / 2 = n^2 / 2 + n / 2。根据上文说的 大O推导法 可以知道,此时时间复杂度为 O(n^2)。

void aFunc(int n) {
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            printf("Hello World\n");
        }
    }
}

 场景5:T(n) = T(n - 1) + T(n - 2),递归调用。

        当T(0) = T(1) = 1,同时 T(n) = T(n - 1) + T(n - 2) + 1,这里的 1 是其中的加法算一次执行。
        当 T(n) = T(n - 1) + T(n - 2) 是一个斐波那契数列,通过归纳证明法可以证明,当 n >= 1 时 T(n) < (5/3)^n,同时当 n > 4 时 T(n) >= (3/2)^n。
所以该方法的时间复杂度可以表示为 O((5/3)^n),简化后为 O(2^n)。

long aFunc(int n) {
    if (n <= 1) {
        return 1;
    } else {
        return aFunc(n - 1) + aFunc(n - 2);
    }
}

最后举过一个例子:

算法A的相对时间规模是T(n)= 100n,时间复杂度是O(n)

算法B的相对时间规模是T(n)= 5n^2,时间复杂度是O(n^2)

算法A运行在小灰家里的老旧电脑上,算法B运行在某台超级计算机上,运行速度是老旧电脑的100倍。

那么,随着输入规模 n 的增长,两种算法谁运行更快呢?

640?wx_fmt=png

从表格中可以看出,当n的值很小的时候,算法A的运行用时要远大于算法B;当n的值达到1000左右,算法A和算法B的运行时间已经接近;当n的值越来越大,达到十万、百万时,算法A的优势开始显现,算法B则越来越慢,差距越来越明显。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值