美文网首页
复杂度分析

复杂度分析

作者: 我阿郑 | 来源:发表于2021-12-14 19:25 被阅读0次

一、什么是数据结构?

数据结构是计算机存储、组织数据的方式

image.png

二、什么是算法?

算法是用于解决特定问题的一系列的执行步骤。

使用不同算法,解决同一个问题,效率可能相差非常大。比如:

求第 n 个斐波那契数(fibonacci number)

三、如何评判一个算法的好坏?

一般从以下维度来评估算法的优劣:

  • 正确性、可读性、健壮性(对不合理输入的反应能力和处理能力)
  • 时间复杂度(time complexity):估算程序指令的执行次数(执行时间)
  • 空间复杂度(space complexity):估算所需占用的存储空间

一般用大O表示法来描述负责度,它表示的是数据规模n对应的复杂度
大O表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的执行效率。

四、时间复杂度

引出重要概念,所有代码的执行时间 T(n)每行代码的执行次数 n成正

T(n) = O(f(n))
它表示一个算法的渐进时间复杂度。
其中 f(n) 表示代码执行次数之和
O表示正比例关系

// 来看一个例子:
for (int i = 1; i <= n; i++) {
    x++;
}

我们知道这个for loopn个循环,假设其中x++ 计算的消耗是一个单位
第一次循环是1单位
第二次循环是2单位
所以整个循环语句就要消耗n个单位
可以发现,消耗的单位时间随着循环的次数而变化
循环次数为1,时间为1单位
循环次数为10,时间为10单位
循环次数为n,时间为n单位
所以这个算法的「时间复杂度」可以表示为:T (n) = O(n)

// 再看一个例子:
for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
        x++;
    }
}

在外层循环中,i 总共需要n层循环,在每一次内层循环中,j 也会循环n次。那么两个循环语句的复杂度就是 O(n^2)

如果我们将这两个算法合并到一起:

for (int i = 1; i <= n; i++) {
    x++;
}
for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
        x++;
    }
}

整个算法复杂度就变为 O(n + n2),在n无限大的情况下,可以简化为 O(n2)

斐波那契数列 - 递归复杂度分析

// O(2^n)
public static int fib1(int n) {
    if (n <= 1) return n;
    return fib1(n - 1) + fib1(n - 2);
}
image.png

忽略常数、系数、低阶:

9 >> O(1)
2n + 3 >> O(n)
n2 + 2n + 6 >> O(n2)
4n3 + 3n2 + 22n + 100 >> O(n3)
写法上,n3 等价于 n^3

常见的时间复杂度

image.png

对数阶的一些细节

先看一下对数定义:

如果ax次方等于N(a>0,且a不等于1),那么数x叫做以a为底N的对数,记作x = log a^N. 其中,a叫做对数的 [底数],N叫做[真数]

❤️ 在计算机信息学中,提到对数,一般就是以2为底

比如:
log2^8 = 3
log2^16 = 4
...
log3^9 = 2
log4^16 = 2
我们在衡量复杂度的时候,对于对数阶来说,一般都会忽略底数

log2^n, log3^n, log4^n ...  都统统成为logn

第一次听说 O(log n) 时间复杂度可能是在学二分搜索算法的时候;
在最好情况下二分搜索的时间复杂度是 O(1),最坏情况(平均情况)下 O(log n), 直接来看O(log n)情况

已知有 16 个元素的有序数组,举个最坏情况的例子,比如我们要找的是数字 13

image.png

选中间的元素作为中心点(长度的一半)

image.png

通过比较发现,13 小于中心点,所以不用考虑数组的后一半,只考虑前一半

image.png
image.png
image.png
image.png

重复这个过程,每次都寻找子数组的中间元素,每次和中间元素比较都会使搜索范围减半
所以为了从 16 个元素中找到目标元素,我们需要把数组平均分割 4 次, 也就是说:

image.png

如果有 n 个元素:

image.png

归纳简化下公式:

image.png

等式两边同时乘以 2^k

image.png
image.png

最终得到:


image.png

对数形式

image.png

最终得出二分搜索算法在最坏的情况下时间复杂度就是O(logn)级别的

时间复杂度分析

1. 常数阶O(1)

只要没有循环或递归等复杂逻辑,无论代码执行多少行,代码复杂度都为O(1),如下:

int x = 0;
int y = 1;
int temp = x;
x = y;
y = temp;

上述代码在执行的时候,所消耗的时间不会随着特定变量的增长而增长,即使有几万行这样的代码,我们都可以用O(1)来表示它的时间复杂度。

2.线性阶O(n)

上述的例子中讲解过O(n)的算法:

for (int i = 1; i <= n; i++) {
    x++;
}

在这段代码中,for循环会执行n遍,因此计算消耗的时间是随着n的变化而变化,因此这类代码都可以用O(n)来表示其时间复杂度

3.对数阶O(logN)

int i = 1;
while(i < n) {
    i = i * 2;
}

在上面的循环中,每次i都会被乘以2,也意味着每次 i 都离 n 更进一步。那需要多少次循环 i 才能等于或大于 n 呢,也就是求解2的x次方等于n,答案x=log2^n。也就是说循环 log2^n次之后,i会大于等于n,这段代码就结束了。所以此代码的复杂度为:O(logN)

4.线性对数阶O(nlogN)

线性对数阶O(nlogN)很好理解,也就是将复杂度为O(logN)的代码循环n遍:

for(int i = 0; i <= n: i++) {
    int x = 1;
    while(x < n) {
        x = x * 2;
    }
}

因为每次循环的复杂度为O(logN),所以n * logN = O(nlogN)

5.平方阶O(n²)

在上面的例子也讲过,O(n²)就是将循环次数为n的代码再循环n遍:

for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
        x++;
    }
}

O(n²)的本质就是n * n,如果我们将内层的循环次数改为m:

for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
        x++;
    }
}

复杂度就变为 n * m = O(n * m)

关于一些更高的阶级比如O(n³)或者O(n^k),我们可以参考O(n²)来理解即可,O(n³)相当于三层循环,以此类推

空间复杂度分析

「空间复杂度」并不是用来计算程序具体占用的空间。
随着问题量级的变大,程序需要分配的内存空间也可能会变得更多,而「空间复杂度」反映的则是内存空间增长的趋势

常用的空间复杂度

比较常用的空间复杂度有:O(1)、O(n)、O(n²),我们一般用 S(n) 来定义「空间复杂度」

1.O(1)空间复杂度

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

int x = 0;
int y = 0;
x++;
y++;

其中x, y所分配的空间不随着处理数据量变化,因此「空间复杂度」为 O(1)

2.O(n)空间复杂度

以下的代码给长度为n的数组赋值:

int[] newArray = new int[n];
for (int i = 0; i < n; i++) {
    newArray[i] = i;
}

在这段代码中,我们创建了一个长度为 n 的数组,然后在循环中为其中的元素赋值。因此,这段代码的「空间复杂度」取决于 newArray 的长度,也就是 n,所以 S(n) = O(n)

「时间复杂度」和「空间复杂度」这两个复杂度反映的是:
随着问题量级的增大,时间和空间增长的趋势。学会了复杂度的分析,我们就可以对比算法之间的优劣势啦~

参考文档

时间空间复杂度分析「数据结构和算法2」

相关文章

  • map:169.求众数(投票算法)

    求众数 哈希Map 复杂度分析 时间复杂度:O(N) 空间复杂度: O(N) 投票算法 复杂度分析

  • 复杂度分析

    为什么需要复杂度分析? 大O复杂度表示法 时间复杂度分析 常见复杂度量级 复杂度量级简单说明 空间复杂度 时间复杂...

  • 针对封装数组的简单复杂度分析

    完成了数组的封装之后我们还需对其进行复杂度分析:此处的复杂度分析主要是指时间复杂度分析,算法的时间复杂度反映了程序...

  • 四、复杂度分析& 动态数组的缩容

    复杂度分析 这里分析之前实现的ArrayList和LinkedList的增删改查的复杂度。分析复杂度是要从下面三个...

  • 一个好的算法如何测评

    一个算法的好坏可以根据复杂度分析来测评. 复杂度分析包括时间复杂度和空间复杂度. 1.时间复杂度 需要考虑: 1)...

  • 数据结构与算法 复杂度分析

    复杂度:时间复杂度和空间复杂度。复杂度的分析是学习数据结构与算法的基础! 极简概述 复杂度的分析已经有很多很好...

  • 数据结构与算法学习-复杂度分析

    前言 这一篇笔记主要记录总结了什么是算法复杂度?、为什要做算法复杂度分析?、如何做算法复杂度分析?、常用的复杂度级...

  • 数据结构-复杂度分析

    为什么需要复杂度分析? 复杂度分析实在太重要了。复杂度分析是整个算法学习的精髓,只要掌握了它,数据结构和算法的内容...

  • 算法复杂度分析

    复杂度分析包括: 时间复杂度分析 空间复杂度分析 事后统计法 我们常用事后统计法来统计效率,这种方法也存在一些问题...

  • 模块2作业 朋友圈高性能复杂度

    分析一下微信朋友圈的高性能复杂度 【作业要求】对照模块 2 讲述的复杂度分析方法,分析微信朋友圈的复杂度;针对各个...

网友评论

      本文标题:复杂度分析

      本文链接:https://www.haomeiwen.com/subject/sloefrtx.html