从小卖部批发辣条到算法复杂度分析

简介: 从小卖部批发辣条到算法复杂度分析

从小卖部批发辣条到算法复杂度分析

概览

1.为什么需要复杂度分析2.大O复杂度表示法3.时间复杂度4.空间复杂度

为什么需要复杂度分析

复杂度分析对数据结构和算法来说很重要,据说占了大半的江山,不知道实际是不是这样子的。我们先来聊聊为什么要学习复杂度分析,最直接的说,我们把代码运行一遍,不就得到结果了,而且比分析出来的结果还要准确。我们先从小卖部聊起,因为学校的规定,所有的零食都需要从学校门口运到宿舍,这时候我们需要最高效的运输方法,每次运送多少,运送多少次。这时候难道我们还要去实际体验几趟吗?来回把东西搬运几次,这第二天要上校园头条了:六栋门口惊现矿水哥,来回抱着几提矿泉水往返宿舍和校园门口……代码的运行速度受限于电脑配置,就像邮寄东西的速度受限于快递,我们接着从豆豆的小卖部下手,家在海南的小伙伴在小卖部订购了一箱干脆面,豆豆同学使用快递寄送,可选择的快递有顺丰和邮政,顺丰第二天到,邮政下周到,你说这个复杂度是二还是七呢?同一个算法同一个机器的结果一定是一样的吗?假定小卖部使用同一个小拖车运货,小拖车标准承载重量为2Kg,运货路线使用A路线不改变,供应商第一次送来1KG的辣条,运送了一次。第二次送来了5KG的辣条,小拖车拼死拼活超载两趟运完了。请问一下,这个小拖车A路运货的复杂度怎么讲呢?

这时候,如果有一个能估算出不同选择的执行效率的方法是不是更好呢?


大 O 复杂度表示法

假设我们对实际的情况进行抽象处理,不考虑复杂的显示情况,把一些条件简单化,得出一个方案或者一段代码的执行时间。


假设一个方案里面的每一步的消耗时间都是一样的,每一步操作都是1个时间单位,那么小卖部每次用小拖车搬货就分为三步:装箱,运输,卸货。每次补货需要消耗的时间就跟这三步走的次数相关了。一个方案的执行时间与每个拆分步骤的执行次数成正比。


从小卖部回到代码上来,一段代码的运行的时间也和每行代码的运行时间相关,以下面的代码为例:

public void sum(int n){
    int sum = 0;
    for(int i = 1;i <= n; ++i){
        sum = sum + i;
    }
    System.out.println(sum);
}

忽略细节上的差异,假定每一句代码的执行时间为1时间单位t,上面这段代码的总共时间消耗是多少呢?


这段代码有两次初始化操作,分别是 i 和 sum,消耗时间就是 2 * t ;


i <= n、 ++i 和 sum = sum + i 这三段代码处于循环之中,执行时间和循环次数 n  相关,执行时间可以记作 3n 。这一段代码的执行时间就可以记作3n+2。


我们用公式表达出来就如下所示:

T(n) = O(f(n)) = O(3n+2)

这种表达方式也就是大O时间复杂度表示法,这种方式并不能表达出来代码的实际执行时间,表达的是代码执行时间随数据规模增长的变化趋势,所以也称作渐进时间复杂度,简称时间复杂度。

当表达式中的n越来越大时,整个表达式结果就会由其中一个变化最大的一部分的影响, 这时候我们就可以忽略掉一些影响不大的参数,比如表达式中的常量、系数等,这时候上面的表达式就可以简单记为:T(n)=O(n)


当我们遇到更加复杂的表达式时,例如遇到包含平方,三次方这种更高级的存在时,我们也可以把低阶去除掉,比如:T(n) = O(3n²+2n+1),这时候就可以写成T(n) = O(n²)

时间复杂度分析

我们在上面已经讲过了大O复杂度的表示方式,接下来补充一些简单的分析方法。

1. 相同变化级別寻找最大量级的代码块

相对于整个结果来说,我们可以根据实际忽略掉表达式中的常量、系数、低阶等,最终的结果相当于最大阶的量级。就像搬货的过程中,去了一趟厕所,整个搬运零食的过程就可以忽略掉这次上厕所的时间,只记搬运货物的次数消耗的时间。


依旧使用上面的代码,我们用这个方法分析一下:

public void sum(int n){
    int sum = 0;
    for(int i = 1;i <= n; ++i){
        sum = sum + i;
    }
    System.out.println(sum);
}

只关注运算量级最大的那一部分,是不是只需要关注循环的那一部分就可以了,这时候忽略系数,只要记作O(n),也就是这段代码的时间复杂度。


当一段代码包含多个代码块时,如果变化的量都相同时,我们只需变化量最大的那一块。比如卡卡吃辣条,吃n包辣条,吃完以后需要喝n/2杯矿泉水,那么这个时间复杂度该怎么表示呢?

2. 不同变化级别的代码段求和

同样是吃辣条,豆豆吃了n包辣条,然后喝了m杯水,假设吃一包辣条和喝一杯水的时间相同,那么此时的时间复杂度是多少呢?在不确定m和n到底哪个比较大的情况下,我们就要把两个值加起来,那么这个复杂度就是O(n+m)


转化为代码如下所示:

public void sum(int n, int m){
    int sum = 0;
    // 豆豆吃了n包辣条
    for(int i = 1;i <= n; ++i){
      sum = sum + n;
    }
         // 豆豆喝水的次数
    for(int j = 1;i <= m; ++i){
      sum = sum + m;
    }
    System.out.println(sum);
}

3. 不同变化级别的嵌套代码求积

比如,豆豆去搬运辣条,从校门口到寝室的次运输次数记做n,每次搬运到寝室以后,不急着去搬运第二趟,还要休息一会,会把运到的辣条进行分类,放到指定的位置,这时候分类的操作需要的时间记做m,那么这么一次完整的操作,总的下来的复杂度就是O(n*m)。

转化为代码如下所示:

public void sum(int n, int m){
    int sum = 0;
    // 豆豆搬运的辣条次数
    for(int i = 1;i <= n; ++i){
      // 豆豆分类辣条的次数
      for(int j = 1;i <= m; ++i){
        sum = sum + i * m;
      }
    }
    System.out.println(sum);
}

常见的算法复杂度

我们经常接触的算法中有一些常见的算法复杂度需要了解一下,如下图所示:

image.png



在上面的复杂度里面,可以划分为多项式量级非多项式量级两种,非多项式量级只有两个:指数阶阶乘阶

简单的聊聊其中的两个,常量阶和对数阶。

常量阶O(1)

常量阶并不真的就是1,它是一种指代,只要能确定代码的运行的次数,并不会随着数据量的提升而变的更复杂,这就是常量阶的复杂度,简称就是O(1)

对数阶O(logn)

对数阶的复杂度不容易理解,但这个也是很常见的算法。很简单的来说,豆豆要把辣条放到货架上,每只手可以拿一包辣条,一次就能放上去两包辣条,那么n包辣条需要多少次呢?这个算不算对数阶段的操作呢?


转化成对应的代码如下所示:

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


这个掰下手指头算一算,是不是一个很熟悉的操作,好像在哪见过。

image.png


不管这个底数是2还是10,当我们使用大O标记复杂度时,都会记为O(logn),这是为什么呢?


在最最最开始的时候,我们讲过,大O方法表示复杂度的时候,是可以忽略系数的,那么这个算法就可以做一次转换。

image.png


如上图所示,忽略系数的情况下,不管是几为底的对数阶时间复杂度,最终的结果都是一样的,所以最后被记做为O(logn),那么O(nlogn)也是同样的道理。

空间复杂度

相对来说空间复杂度就会简单很多,时间复杂度全称是渐进时间复杂度,表示算法的执行时间与数据规模之间的增长关系。同样的道理,空间复杂度就是渐进空间复杂度,表示算法的存储空间与数据规模之间的增长关系。


空间的复杂度主要是计算变量的存储空间大小,辣条运送到小卖部的路途中是不是需要地方存储, 这时候是不是需要一些空间,这个空间的复杂度理解起来和时间复杂度类似。甚至来说会更简单。


空间复杂度主要集中在变量的占用,在同样忽略常量、系数、低阶的情况下,我们得到一个大概的空间复杂度。常见的空间复杂度主要包括:常量阶O(1)、 线性阶O(n)、 O(n²),这里少了对数阶复杂度,学习起来也没有那么难。


目录
相关文章
|
3月前
|
机器学习/深度学习 算法 搜索推荐
从理论到实践,Python算法复杂度分析一站式教程,助你轻松驾驭大数据挑战!
【10月更文挑战第4天】在大数据时代,算法效率至关重要。本文从理论入手,介绍时间复杂度和空间复杂度两个核心概念,并通过冒泡排序和快速排序的Python实现详细分析其复杂度。冒泡排序的时间复杂度为O(n^2),空间复杂度为O(1);快速排序平均时间复杂度为O(n log n),空间复杂度为O(log n)。文章还介绍了算法选择、分而治之及空间换时间等优化策略,帮助你在大数据挑战中游刃有余。
95 3
|
5月前
|
人工智能 算法 BI
第一周算法设计与分析 D : 两面包夹芝士
这篇文章介绍了解决算法问题"两面包夹芝士"的方法,通过找出两个数组中的最大最小值,计算这两个值之间的整数个数,包括特判不存在整数的情况。
|
3天前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
13天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
25 6
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
65 1
|
3月前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
3月前
|
算法
PID算法原理分析
【10月更文挑战第12天】PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。
|
3月前
|
人工智能 算法 前端开发
无界批发零售定义及无界AI算法,打破传统壁垒,累积数据流量
“无界批发与零售”是一种结合了批发与零售的商业模式,通过后端逻辑、数据库设计和前端用户界面实现。该模式支持用户注册、登录、商品管理、订单处理、批发与零售功能,并根据用户行为计算信用等级,确保交易安全与高效。
|
4月前
|
算法 搜索推荐 开发者
别再让复杂度拖你后腿!Python 算法设计与分析实战,教你如何精准评估与优化!
在 Python 编程中,算法的性能至关重要。本文将带您深入了解算法复杂度的概念,包括时间复杂度和空间复杂度。通过具体的例子,如冒泡排序算法 (`O(n^2)` 时间复杂度,`O(1)` 空间复杂度),我们将展示如何评估算法的性能。同时,我们还会介绍如何优化算法,例如使用 Python 的内置函数 `max` 来提高查找最大值的效率,或利用哈希表将查找时间从 `O(n)` 降至 `O(1)`。此外,还将介绍使用 `timeit` 模块等工具来评估算法性能的方法。通过不断实践,您将能更高效地优化 Python 程序。
74 4
|
4月前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
78 1

热门文章

最新文章