数据结构与算法 #时间复杂度 #空间复杂度

文章目录

前言

一、算法的复杂度

二、时间复杂度

三、空间复杂度

四、例题

1、例1:冒泡排序

2、例2:

3、例3:

4、例4: 二分查找

5、例5: 阶乘

6、例6: 斐波那契

五、常见算法复杂度

总结


前言

路漫漫其修远兮,吾将上下而求索;


一、算法的复杂度

当我们编写了一个 算法之后,如何衡量这个算法到底是好还是坏呢?

  • 算法在运行的过程中最重要的指标是此算法快不快以及其在运行的过程之中会消耗多少空间,即时间复杂度与空间复杂度;

时间复杂度:衡量一个算法运行的运行快慢

空间复杂度:衡量一个算法运行时所需要的额外空间

注:在早期计算机的发展中,由于计算机的存储容量小,所以比较关注空间复杂度;但是随着计算机行业的快速发展(摩尔定律:每18个月计算机的硬件便会翻一倍)内存越来越大并且越来越便宜,于是现在就不那么关注空间复杂度了;

二、时间复杂度

算法的时间复杂度是一个函数,它定量地描述了该算法的运行时间(该算法执行所消耗的时间);理论来说,时间复杂度是不可能算出来的,只有当此程序在机器上跑起来的时候,才可以知道此算法运行的时间;

注:此处所述的函数,并不是在C语言中的那个函数,而是在数学之中的函数表达式

时间复杂度为什么不是用来计算该代码运行了多少秒呢?

  • 因为并没有一个准确的方式去计算一个算法运行了多少秒;相同的算法在不同的机器上,是有差异的,即一个算法的执行速度受环境的影响;

那么时间复杂度究竟计算的是什么呢?

  • 计算的是一个算法之中基本操作(代码)的执行次数;哪个算法的基本操作的执行次数少,那么该算法便会更快;

一个算法花费的时间与其语句的执行次数成正比例,算法中的基本操作的执行次数为算法的时间复杂度;(找到某条基本语句与问题规模N之间的数学表达式)并且在计算的时候也不需要计算该算法精确的执行次数,只需要大概的执行次数便可以了,那么这里便会使用到大O的渐近表示法

何为大O的渐进表示法?

  • 大O符号(Big O notation): 用于描述函数渐进行为的数学符号

推导大O阶方法:

  • 常数1取代运行时间中的所有加法常数
  • 在修改后的运行次数函数中,只保留最高阶项
  • 如果最高阶项存在并且不是1,则去除与这个项目相乘的常数(让最高项的系数为1)

最后所得到的结果便是大O阶;

例子代码如下:

void Func1(int N)
{int i = 0;int j = 0;int k = 0;int count = 0;for (i = 0; i < N; i++){for(j = 0; j < N; j++){count++;}}for(k = 0; k < 2 * N; k++){count++;}int M = 10;while (M--){count++;}printf("%d\n", count);
}

倘若要准确地计算上述代码的执行次数 = N^2 + 2*N + M (其中M为10,是一个常数)

根据推导大O阶规则,则会得到该算法的时间复杂度为:O(N^2) 

从上例,我们可以得知,大O的渐进表示法去掉了那些对结果影响不大的项,保留了对结果影响最大的项(可以采用数学极限的思想)

计算时间复杂度的技巧:可以大致计算”精确“的,然后再在此基础上利用推导大O阶规则;

另外在有些算法中时间复杂度存在最好、平均、最坏的情况:

最好的情况:任意输入规模的最小运行次数

平均的情况:任意输入规模的期望运行次数

最坏的情况:任意输入规模的最大运行次数

我们在计算一个算法的时间复杂度的时候,一般关注的是其最坏的情况;

三、空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时额外占用存储空间大小的量度;

空间复杂度并不是精确计算这个程序占用了多少byte 的空间,而是采用大O渐进表达式的方式来计算在运行过程中额外运用了多少空间

注:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器的信息等),在编译期间便已经确定好了,因此空间复杂度主要通过函数在运行的时候显式申请的额外空间来确定的;

换句话说,空间复杂度的计算的是在运行过程中额外占用存储空间的变量个数,倘若在运行的过程中建立栈帧,这也是要算入的;并且遵循推导大O阶的规则;

四、例题

1、例1:冒泡排序

分析:

分析时间、空间复杂度的核心是梳理清楚整个代码的逻辑;

显然冒泡排序的逻辑为:整个冒泡排序会有两个循环,一个控制循环的趟数,一个控制一趟排序之中相邻两元素所要比较的次数,并且每次冒完一趟均会确定好一个元素的最终位置,故而每冒完一趟下一趟冒泡均会减少一次比较;

如此大致看起来,冒泡排序的基本代码执行次数为一个等差数列之和,而复杂度采取的是大O渐进表达式的估算法,即相加的常数项为1,只保留最高项并且最高项的系数为1,显然其时间复杂度变为O(N^2);

而整个冒泡排序的过程都只是在一定大小的数组进行的,并没有使用额外的空间,所以其空间复杂度为O(1);

2、例2:

分析:

注:大O渐进表达式用的未知数用的是N,有时也可以是M、K等字母;

为什么此处的时间复杂度为O(N+M) ?

  • 因为在此处N 与 M 均为未知数,并不知道谁大谁小;按照推导大O阶的方式于是乎便得到了O(N+M);

倘若此处有N 与 M 的大小关系,其时间复杂度便会不同:

而对于此算法的空间复杂度,整个算法的执行依靠的是几个变量,并没有使用额外开辟的空间,故而其空间复杂度为O(1);

3、例3:

分析:此算法会受其参数的影响,即参数字符串的长度会影响这个算法完成任务所需要的时间;既然字符串的长度不清楚,便可以将字符串的长度看作为N

当在一个字符串中查找一个字符额时候,可能一上来便找到了,也有可能在中间位置才找到,亦或许在字符串的结尾才能找到……显然会在字符串的哪个位置找到目标字符这也是未知的;

对于时间复杂度来说,关注的使这个算法最坏的情况,故而此算法的时间复杂度为:O(N) ;

呼应上文所述概念:

平均情况有什么特殊意义?

  • 大部分算法并不会看其平均情况,即使看平均情况也是看一下这个算法大概会执行多少次;例如,希尔排序,很少出现最坏的情况,并且最坏的情况难以计算,故而希尔排序这个算法会看其平均情况;(其平均情况的计算会使用到一个非常复杂的数学公式进行计算);

常见的平均情况的计算方式:

1、(最好情况+ 最坏情况)/2

2、按照概率计算(比较复杂)

4、例4: 二分查找

分析:注意二分查找的逻辑;

二分查找会利用到三个指针,一个指向数组的最左,一个指向数组的最右,另外一个指向中间位置;

  • 让中间位置的数据与x 相比较,当小于mid 的时候便缩小一半的查找范围,调整left 到mid 右边的位置;倘若大于mid 也会缩小一半的查找范围,调整 right 到 mid 到左边的位置上;而如若mid 所对应得数据等于所要查的数据x ,便代表着查找成功;

显然,我们也不知道所找的数据会在整个数组的哪个位置,存在最好、平均、最坏的情况;二分查找的特性,每次查找没找到便会缩小一半的查找范围,也就是说缩小了几次查找返回便意味着查找了多少次;

最坏的情况就是在这个数组中找不到所找数据,假设我们找了k 次,那么 其中n 为这个数组中元素的个数,由式可得 : ;所以此算法的时间复杂度为:

当然,由于该二分查找算法并没有使用额外的空间,于是其空间复杂度为:O(1)

有关二分查找的知识补充:

二分查找的查找效率非常高效,但是这个算法是有缺陷的,即必须在有序数组上才能进行查找;倘若面对所要查找的数据是无序的,那么首先就得排序,然而排序也是一个非常消耗的过程;

于是乎便有了后面的 树 --> 二叉树 --> 搜索二叉树 --> 平衡搜索二叉树 --> AVL Tree 、RBTree(红黑树) ,这些算法,无需排序也可以达到二分查找的高效;以及还有更厉害的哈希表、b树系列.[ps:路漫漫……漫漫路……]

5、例5: 阶乘

分析:递归,便会利用到函数栈帧相关知识来理解;

 递归中基本代码的执行次数 = 递归次数 * 每次递归中基本代码的执行次数(看每次递归中有无循环);

 

为什么其空间复杂度为O(N) ?

  • 该算法在执行的时候,额外地开辟了空间 -- 函数栈帧的创建;每调用一个函数,便会创建对应的函数栈帧;函数栈帧的开辟个数与递归的次数有关,而在每个函数栈帧中均会使用常数个空间,根据大O渐进表达式的规则,故而其空间复杂度为O(N),图示如下:

递归需要看栈帧的消耗,即看递归的深度;

6、例6: 斐波那契

分析:

显然,斐波那契函数Fib 也遵循: 递归中基本代码的执行次数 = 递归次数 * 每次递归中基本代码的执行次数(看每次递归中有无循环);

如上图所示,Fib 的递归次数为一个等比数列求和并减去一个X(X为假设缺少的常数个递归次数,远小于前面的等比数列求和的所得值)

根据大O渐进表达式,因为(1+X)远小于 2^n, 所以(1+X)可以忽略,故此算法的时间复杂度为: O(2^N);

 空间复杂度:

同理,递归的空间复杂度看函数栈帧开辟的次数,即递归的深度 再乘以每次递归中额外使用的空间;Fib 的递归深度以大O渐进表达式为:O(2^N) ; 并且每次递归中并没有使用额外的空间,那么Fib 的空间复杂度便为 O(2^N)?

其实不然,因为 2^N 增长地很快,一般情况在Linux 下,建立一个进程的栈,只有8M ;倘若此算法的空间复杂度为 O(2^N) , 栈肯定会溢出;

实际上,斐波那契函数(递归)是会重复利用空间的,并且不累计;

如上如所示,会先计算一条分支()然后再计算其他分支;即斐波那契函数再创建栈帧的时候会先建立”一支“的栈帧,直到这一支结束便销毁此支创建的函数栈帧……然后再继续调用下一支以实现重复利用空间;那么此时的计算空间复杂度便是看的递归的深度,即最多创建了多少函数栈帧,故而斐波那契函数的空间复杂度为:O(N);

注:递归是一条”支路“递归结束返回了再退回去递归另外一条”支路“;

五、常见算法复杂度

常见算法的时间复杂度包括

  • 常数时间复杂度(常数阶): O(1)
  • 对数时间复杂度(对数阶): O(log n)
  • 线性时间复杂度(线性阶): O(n)
  • 线性对数时间复杂度(线性对数阶): O(n log n)
  • 平方时间复杂度(平方阶): O(n^2)
  • 立方时间复杂度(立方阶): O(n^3)
  • 指数时间复杂度(指数阶): O(2^n)


总结

1、时间复杂度与空间复杂度的计算基于大O渐进表达式

2、大O符号(Big O notation): 用于描述函数渐进行为的数学符号

推导大O阶方法:

  • 常数1取代运行时间中的所有加法常数
  • 在修改后的运行次数函数中,只保留最高阶项
  • 如果最高阶项存在并且不是1,则去除与这个项目相乘的常数(让最高项的系数为1)

3、分析时间、空间复杂度的核心是梳理清楚整个代码的逻辑;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/148844.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

5个适合教师的AI工具,智能辅助,提升效率,让老师们工作更轻松!

随着人工智能技术的蓬勃发展&#xff0c;我们正步入一个由AI引领的变革时代&#xff0c;它不仅重塑了多个行业的面貌&#xff0c;更激发了我们对未来无限可能的想象。面对这一趋势&#xff0c;我们不应仅仅聚焦于其带来的挑战与冲击&#xff0c;而应积极拥抱变化&#xff0c;探…

猫咪掉毛背后的隐秘原因?除毛除臭宠物空气净化器双管齐下!

作为一个二胎家庭&#xff0c;两只猫咪&#xff0c;除了卖萌加倍之外&#xff0c;拉屎需要排队之外&#xff0c;家里最不缺就是毛了。作为一个名鼻炎患者真的很难顶。感受一下40度高温的养猫人&#xff0c;给掉毛怪疏毛浮毛飘飘&#xff0c;逃不过的饮水机&#xff0c;各个角落…

Deep Guided Learning for Fast Multi-ExposureImage Fusion

Abstract 我们提出了一种快速多重曝光图像融合&#xff08;MEF&#xff09;方法&#xff0c;即 MEF-Net&#xff0c;用于任意空间分辨率和曝光次数的静态图像序列。 我们首先将输入序列的低分辨率版本提供给全卷积网络以进行权重图预测。 然后&#xff0c;我们使用引导滤波器联…

HTML5 Video标签的属性、方法和事件汇总,以及常用视频插件推荐

&#x1f680; 个人简介&#xff1a;某大型国企资深软件研发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

【Unity3d Shader】毛玻璃效果

毛玻璃也叫​磨砂玻璃​:是用物理或化学方法处理过的一种表面粗糙不平整的半透明玻璃。 毛玻璃成像原理:毛玻璃表面不平整,光线通过毛玻璃被反射后向四面八方射出去(因为毛玻璃表面不是光滑的平面,使光产生了漫反射),折射到视网膜上已经是不完整的像,于是就看不清楚(…

关于 mybatis-plus-boot-starter 与 mybatis-spring-boot-starter 的错误

不是知道你是否 出现过这样的错误 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): 经过各种度娘&#xff0c;无非就是让你检查三种情况 情况一&#xff1a;mapper.xml没有按照传统的maven架构进行放置 情况二&#xff1a;mybatis的配置信…

智能PPT行业赋能用户画像

智能PPT市场在巨大的需求前景下&#xff0c;已吸引一批不同类型的玩家投入参与竞争。从参与玩家类型来看&#xff0c;不乏各类与PPT创作有关的上下游企业逐步向智能PPT赛道转型进入&#xff0c;也包括顺应生成式AI技术热潮所推出的创业企业玩家。当前&#xff0c;智能PPT赛道发…

【网络协议栈】传输层的意义 和 UDP协议结构的解析(内含逻辑图解通俗易懂)

绪论​ “六年之约—jack”。本章是网络协议栈第二个主要模块 传输层&#xff0c;传输层在网络层中是非常重要的&#xff0c;他主要通过储存双方的端口记录数据的来源以及数据最终的去处&#xff0c;并且能一定的保证数据传输到达&#xff0c;以及快速高效的传递。本章主要讲到…

(附源码)基于django的电力工程作业现场物资管理系统的设计与实现-计算机毕设 22067

基于django的电力工程作业现场物资管理系统的设计与实现 摘 要 随着电力工程的快速发展&#xff0c;作业现场物资管理成为保障工程进度和质量的关键环节。本文旨在设计并实现一个基于Django框架的电力工程作业现场物资管理系统&#xff0c;以提高物资管理的效率和准确性。该系统…

约克VRF中央空调的优点不止一点点!

约克VRF中央空调的优点不止一点点&#xff01;      整体造型简约大方&#xff0c;隐入吊顶里刚刚好&#xff0c;高级又很有氛围感。      用约克小方App就能自由操控&#xff0c;忘记关空调再也不用跑回来关啦&#xff0c;使用起来hin方便&#xff0c;懒人大喜&#x…

MySQL如何实现并发控制?(上)

前言 最开始学习数据库的时候都会被问到一个问题&#xff1a;“数据库系统相比与文件系统最大的优势是什么&#xff1f;”。具体的优势有很多&#xff0c;其中一个很重要的部分是&#xff1a;数据库系统能够进行更好的并发访问控制。 那么&#xff0c;数据库系统到底是怎么进…

通过 Flink 的火焰图定位反压

在 Apache Flink 中&#xff0c;Web UI 提供了丰富的监控工具来帮助用户分析和解决作业性能问题&#xff0c;其中火焰图&#xff08;Flame Graph&#xff09;是用于分析反压问题的一个强有力的工具。反压可能是由于作业中某些算子处理速度过慢&#xff0c;或者资源耗尽导致的。…

【解密 Kotlin 扩展函数】扩展函数的底层原理(十八)

导读大纲 1.1.1 从 Java 调用扩展函数1.1.2 扩展函数无法重载 1.1.1 从 Java 调用扩展函数 在编译器底层下,扩展函数是一种静态方法,它接受接收器对象作为第一个参数 调用它不涉及创建适配器对象或任何其他运行时开销这使得从 Java 使用扩展函数变得非常简单 调用静态方法并传…

使用k8s部署RainLoop-Webmail

说明 * rainloop最新源码官方下载地址&#xff1a;https://www.rainloop.net/downloads/ * 系统要求&#xff1a;https://www.rainloop.net/docs/system-requirements/ * 安装文档&#xff1a;https://www.rainloop.net/docs/installation/ * 更多详细资料请查看官方文档 * do…

HDL coder使用手册

&#x1f4a1; 由于本科毕设女朋友准备使用FPGA完成&#xff0c;因此写这篇文章帮助她快速上手HDL coder的使用&#xff0c;降低前期入门的难度。 支持生成HDL代码的simulink库 名字中含有HDL的库中的模块一般都可以用来生成HDL代码。直接搜索模块名称&#xff0c;比如搜索fir&…

管道检测与识别系统源码分享

管道检测与识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

C++进阶学习——模版进阶

1. 非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成…

寄大件快递用什么物流更便宜,寄20-200公斤大件价格对比

大件货物&#xff0c;大件行李&#xff0c;大件电器用什么物流快递更便宜呢&#xff1f; 新生入学&#xff0c;放寒暑假&#xff0c;新单位入职&#xff0c;搬家换工作的时候&#xff0c;都会遇到大件行李货物要邮寄的情况。这些都属于物流中的寄大件服务&#xff0c;在快递费…

隐私计算相关知识

WOE&#xff08; Weight of Evidence&#xff09;编码 一种在数据分析&#xff0c;尤其是信用评分和欺诈检测等领域中常用的特征编码方法。它的主要目的是将分类变量转换为数值变量&#xff0c;从而使得模型能够更好地理解类别与目标变量之间的关系 IV&#xff08; Informatio…

大数据毕业设计选题推荐-网络电视剧收视率分析系统-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…