算法复杂度

本文详细介绍了算法复杂度的概念,包括时间复杂度和空间复杂度,通过多个代码示例解析了常见的时间复杂度级别,如O(1)、O(n)、O(n^2)、O(n^3)、O(logn)和O(nlogn)等,并解释了如何计算复杂度。此外,还提及了最好、最坏和平均时间复杂度的概念。

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

什么是算法复杂度

算法复杂度,即算法在编写写成可执行程序后,运行时所需要的资源,资源包括时间资源和内存资源

通俗的说,就是执行一段代码所需要的资源(主要是花费的时间以及占用的空间)。

大 O 复杂度表示法

我们记一个算法的复杂度为O(n),表示数据规模为n的情况下,算法使用的时间和空间资源。(也可以理解O(n)描述着,代码执行花费的时间和空间随着n增大变化的趋势)。
算法复杂度从时间上考虑是时间复杂度(快不快),从空间上考虑是空间复杂度(占的内存多不多)。

时间复杂度

1、O(1)

	public void method1(int n){
        int a = 5; // 执行1次
        int b = 6; // 执行1次
        int c =  a + b;// 执行1次
        System.out.println(c);// 执行1次
    }

现在有如上的一段代码,假定每一行执行的时间为“1”,那么上述代码用掉的时间为4。无论n是多大,使用的时间都是4,记为O(n) =4。**当n趋于无限大的时候,这个4可以近似的认为是1。**为什么呢,还记得我们为什么需要算法复杂度吗?我们需要通过时间复杂度来描述这个代码随着n的增大,所需要时间的变化趋势。而常数项对变化趋势影响很小,所以可以认为是O(1)。

2、O(n)

	public void method2(int n){ 
        for (int i = 1 ; i <= n; ++i) {
            System.out.println("haha"); // 第3行
        }
    }

同样假定每一行执行的时间为“1”,那么这个代码要花费多长时间呢。这个时候,我们只需要看第3行执行多少次即可。
这个很轻易的看出这是与n有关的。n=1,那么System.out.println("haha")只要执行1次;n=2,执行2次;n=3,执行3次。当等于n时,执行n次。所以这个时间的变化是成线性上升的,所以O(n)=n;

3、O(n2n^2n2)

	public void method3(int n){
        for (int i = 1 ; i <= n; ++i) { // 第2行   第1层嵌套
            System.out.println("haha"); // 第3行
            for (int j = 1 ; j <= n; ++j) { // 第4行  第2层嵌套
                System.out.println("heihei");// 第5行
            }
        }
    }

老规矩,假定每一行执行的时间为“1”。
上面说过,常数项对变化趋势影响很小,所以可以认为是O(1)。那么同样, 当高次项与低次项同时存在时,比如n3n^3n3 + n2n^2n2 + nnn,只有高次项对变化的趋势影响最大。所以我们只需要保留高次项,删除低次项即可。即:n3n^3n3 + n2n^2n2 + nnn = O(n3n^3n3)(这个不是上述代码的复杂度,还没有开始计算呢,)

注意:1、具有多层嵌套的情况下,从最里层嵌套开始看。
在这里插入图片描述
首先看下2号嵌套,
对于System.out.println("heihei")来说,无论j取什么值,2号for循环的循环体都执行一次,j=2,System.out.println("heihei")执行一次,j=100还是执行一次。j的取值在1到n之间,所以对于整个2号for循环,一共执行了n次(n个1累加)。然后看1号for循环。
当i=1时,1号的循环体执行1+n次(System.out.println(“haha”)1次,2号for循环体n次);i=2时,1好的循环体执行1+n次(System.out.println(“haha”)1次,2号for循环体n次);i=3时,1号的循环体执行1+n次(System.out.println(“haha”)1次,2号for循环体n次)。那么i=n时,1号的循环体1+n次(System.out.println(“haha”)1次,2号for循环体n次)。累加起来就是:∑i=1n1+n\displaystyle\sum_{i=1}^{n} 1+ni=1n1+n = n + n2n^2n2。只保留高次项,所以时间复杂度O(n2n^2n2)。(从此也可以看出,我们其实只需要算出具有最多次嵌套中的代码执行的次数即可。于本例子中就是算出第5行执行多少次就ok了)

4、O(n2n^2n2)

	public void method4(int n){
        for (int i = 1 ; i <= n; ++i) {
            System.out.println("haha");
            for (int j = 1 ; j <= i; ++j) {
                System.out.println("heihei");
            }
        }

    }

在这里插入图片描述
仔细看看,这个和3还是有区别的,2号for里面的n变成i了。
同样先看下2号嵌套,
j=1时,执行1次,
j=2时,执行1次,
当 j=i时,还是执行1次,所以2号for循环执行i次(i个1累加)。
对于1号for循环,
当i=1时,执行1次(因为最终计算复杂度时,会忽略低次项,所以此次直接忽略System.out.println(“haha”)的执行次数,并且上面已经得出2号for循环的执行次数是i,此时i=2,也意味着2号for循环的执行次数是2),
当i=2时,执行2次
当i=3时,执行3次,
当i=n时,执行n次,
所以时间复杂度: ∑i=1ni=\displaystyle\sum_{i=1}^{n} i =i=1ni= n∗(n+1)2\frac{n*(n+1)}{2}2n(n+1) = n2+n2\frac{n^2+n}{2}2n2+n ,忽略低次项以及系数后,为O(n2n^2n2)

5、O(n2n^2n2)

	public void method5(int n){
        for (int i = 1 ; i <= n; ++i) {
            System.out.println("haha");
            for (int j = i ; j <= n; ++j) {
                System.out.println("heihei");
            }
        }
    }

在这里插入图片描述
仔细看看,这个和3还有4相比,又变化了
同样先看下2号嵌套,
j=i时,执行1次,
j=i+1时,执行1次,
当 j=n时,还是执行1次,所以2号for循环执行n-i+1次(n-i+1个1累加)。
对于1号for循环,
当i=1时,执行n次(因为最终计算复杂度时,会忽略低次项,所以此次直接忽略System.out.println(“haha”)的执行次数,并且上面已经得出2号for循环的执行次数是n-i+1,此时i=1,也意味着2号for循环的执行次数是n),
当i=2时,执行n-1次,
当i=3时,执行n-2次,
当i=n时,执行1次,
所以时间复杂度: ∑i=1ni=\displaystyle\sum_{i=1}^{n} i =i=1ni= n∗(n+1)2\frac{n*(n+1)}{2}2n(n+1) = n2+n2\frac{n^2+n}{2}2n2+n ,忽略低次项以及系数后,为O(n2n^2n2)

6、O(n3n^3n3)

	public void method6(int n){
        for (int i = 1 ; i <= n; ++i) {
            System.out.println("haha");
            for (int j = 1 ; j <= i; ++j) {
                System.out.println("heihei");
                for (int k = 1 ; k <= j; ++k) {
                    System.out.println("hehe");
                }
            }

        }

    }

在这里插入图片描述
这次来个3层循环的。
老规矩,3号for的执行次数为j,
那么2号for的循环次数就是n2+n2\frac{n^2+n}{2}2n2+n
对于1号for来说,
i=1,执行12+12\frac{1^2+1}{2}212+1
i=2,执行22+22\frac{2^2+2}{2}222+2
i=3,执行32+32\frac{3^2+3}{2}232+3
i=n,执行n2+n2\frac{n^2+n}{2}2n2+n

所以时间复杂度: ∑i=1ni2+i2=\displaystyle\sum_{i=1}^{n} \frac{i^2+i}{2}=i=1n2i2+i= O(n3n^3n3)

7、O(lognlog nlogn)

	public void method10(int n){
        for (int i = 1 ; i <= n; i*=2) {
            System.out.println("heihei");
        }
    }

当i=log2nlog_2^nlog2n时,跳出循环。
i取值范围((202^020212^121222^222232^323,…,2t2^t2t),(t = log2nlog_2^nlog2n)
所以这个循环一共走log2nlog_2^nlog2n次,即O(log2nlog_2^nlog2n)

8、O(n lognlognlogn)

 public void method8(int n){

        for (int i = 1 ; i <= n; i*=2) {
            System.out.println("haha");

            for (int j = 1 ; j <= n; ++j) {
                System.out.println("heihei");
            }

        }

    }

在这里插入图片描述
2号for的执行次数为n
1号for的i实际的取值范围(202^020212^121222^222232^323,…,2t2^t2t),(t = log2nlog_2^nlog2n)
i=202^020 ,执行n 次,
i=212^121,执行n 次 ,
i=222^222,执行n 次 ,
i=n,执行n 次 ,
所以时间复杂度: ∑x=0log2nn=\displaystyle\sum_{x=0}^{log_2^n} n=x=0log2nn= n*log2nlog_2^nlog2n 。此处直接忽略系数 O(n lognlognlogn)

9、O(n)

	public void method9(int n){
        for (int i = 1 ; i <= n; i*=2) {
            System.out.println("haha");
            for (int j = 1 ; j <= i; ++j) {
                System.out.println("heihei");
            }
        }
    }

在这里插入图片描述
2号for的执行次数为i
1号for的i实际的取值范围(202^020212^121222^222232^323,…,2t2^t2t),(t = log2nlog_2^nlog2n)
i=202^020 ,执行202^020 次,
i=212^121,执行212^121 次 ,
i=222^222,执行222^222 次 ,
i=n,执行n 次 ,
所以时间复杂度: ∑x=0log2n2x=\displaystyle\sum_{x=0}^{log_2^n} 2^x=x=0log2n2x= 20(1−2t)1−2\frac{2^0(1-2^t)}{1-2}1220(12t)
又因为t=log2nlog_2^nlog2n
所以20(1−2t)1−2\frac{2^0(1-2^t)}{1-2}1220(12t) = (n-1)
因此:O(n)

总结:
 没有循环,时间复杂度:O(1)
 有循环:从最内层循环开始,
  1、确定变量取值范围
  2、确定变量每次取值的时间复杂度,记为f(n)
  3、循环相加f(n)
 重复1到3步,即可计算出嵌套循环的时间复杂度

各种时间复杂度比较

上面的例子中,演示了O(1)、O(n)、O(n2n^2n2)、O(n3n^3n3)、O(lognlognlogn)、O(n∗lognn*lognnlogn)
下图是几个复杂度比较
在这里插入图片描述

最好、最坏、平均、均摊时间复杂度

最好时间复杂度:在最理想的情况下,执行这段代码的时间复杂度。就是代码最少执行多少次。
最坏时间复杂度:在最糟糕的情况下,执行这段代码的时间复杂度。就是代码最多------------------------------------执行多少次。

那么在最好与最坏的情况下一定会存在这其他的情况,累加 这些情况下的时间复杂度 乘以 每种情况出现的频率 即可求出平均时间复杂度。
平均时间复杂度: ∑i=1nO(i)∗P(i)\displaystyle\sum_{i=1}^{n} O(i)*P(i)i=1nO(i)P(i).
O(i)O(i)O(i)代表i情况下花费的时间
P(i)P(i)P(i)代表出现i这种情况的概率。

1

    // array表示一个长度为n的数组
    // 代码中的array.length就等于n
    int[] array = new int[n];
    int count = 0;

    void insert(int val) {
        if (count == array.length) {
            int sum = 0;
            for (int i = 0; i < array.length; ++i) {
                sum = sum + array[i];
            }
            array[0] = sum;
            count = 1;
        }

        array[count] = val;
        ++count;
    }

最好时间复杂度:不执行if语句,O(1)
最坏时间复杂度:执行if语句,if语句里面有个for循环,需要执行n次,所以O(n)
平均时间复杂度:对于数据规模n,即取值范围为1,2,3,…,n

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值