【数据结构】时间复杂度与空间复杂度

时间复杂度

算法的时间复杂度并不是指一个代码运行时间的快慢,因为在不同机器上运行的时间肯定不同,因此算法的时间复杂度指的是基本操作的执行次数,他是一个数学意义上的函数。这个函数并不是C语言中那种函数,而是一个数学函数,比如

显然是执行了N方+2N+10次,这个N方假设我们说他是F(N),也就是一个数学函数,那么这个F(N)就是上面这个算法的时间复杂度。但是实际上我们并不一定要计算精确的执行次数,而只需要大概执行次数,因为当N足够大的时候,F(N)的大小主要是由N方决定了,因此我们就可以用N方来近似这个表达式的值。 我们可以使用大O的渐进表示法。

大O渐进表示法的规则如下:

1、用常数1取代运行时间中的所有加法常数。比如要运行一万次是O(1),一亿次也是写作O(1),这是因为CPU的功能很强大,对于我们能写出来的这些常数,在CPU看来都一样,都是瞬间搞定。

2、在修改后的运行次数函数中,只保留最高阶项。

3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。换句话说如果保留的最高阶数系数不是1,通通都写成系数是1,也就是说不管最终决定运算次数的项是2N,3N,1000N,使用大O渐进表示法都写作O(N),如果最后算出来运算次数是2的n-1次方,n-2次方等等,那么时间复杂度是O(2^n)

4.有些算法的时间复杂度存在最好、平均和最坏情况:最坏情况:任意输入规模的最大运行次数(上界)平均情况:任意输入规模的期望运行次数最好情况:任意输入规模的最小运行次数(下界)例如:在一个长度为N数组中搜索一个数据x最好情况:1次找到最坏情况:N次找到平均情况:N/2次找到。在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)

下面来计算一下冒泡排序的时间复杂度

执行的次数也就是for循环一共循环了多少次,可以看到第一次要比较n-1个,第二次要比较n-2个,第n-1次要比较1个,一共执行的次数就是这些全加起来,等差数列求和,因此一共是n(n-1)/2,最高次数是2次方,虽然有系数二分之一,但是要忽略掉,因此他的时间复杂度是O(n方)

注:只是在本例中执行次数就是循环次数,并不是说所有代码的执行次数都是循环次数

计算二分查找的时间复杂度

二分查找的思想就是一次去掉一半,实际上有可能是一次找到了,也有可能是找到剩下最后一个了也没找到,要计算复杂度,我们就要考虑最坏情况,最坏情况就是找到了剩下最后一个元素,这就是最后一次,不管最后剩下的这个元素是不是我们要找的元素,都不会再执行了,假设有n个数,找了x次最后剩下一个了,实际上x就是复杂度,那么x能不能用已知条件表示呢?N除了x个2变成了1,换句话说1乘上x个2也就是2的x次方等于n,x也就是以二为底n的对数,因此二分查找的时间复杂度是以二为底n的对数。

阶乘递归的时间复杂度

第一次调用Fac(N),而Fac(N)又会调用Fac(N-1),以此类推直到Fac(1)调用Fac(0),就开始一直返回了,因此实际基本语句执行了N+1次,时间复杂度是O(N)

把代码稍作修改,这次时间复杂度是多少?

仍然是Fac(N)调用Fac(N-1),Fac(N-1)调用Fac(N-2)以此类推直到Fac(1)调用Fac(0),但是在每次调用之前都有一个循环语句,在Fac(N)里执行了N次,在Fac(N-1)里执行了n-1次......在Fac(1)里执行了1次,因此基本操作一共执行了N(N+1)/2次,所以时间复杂度是O(N方)

计算斐波那契递归的时间复杂度

一生二,二生四,直到调用参数是2或者1的时候才可以返回值,因此右边的会先开始返回值,左边的后返回,最终形状大概是缺一块的三角形

形状类似这样

假设是一个满的三角形,那么最终应该有2的零次方一直加到2的N-2次方,这样一个等比数列求和,结果是2的N-1次方-1,实际上没有什么多项,还应该减去这个灰色的三角形,但是当N足够大的时候,实际上这个灰色三角形非常的小,可以忽略不计,因为复杂度考虑的是量级,是一个大约的数,因此我们就用这个完整三角形形状的个数来表示,因此斐波那契递归的时间复杂度是O(2^N)

空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度, 空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。

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

计算冒泡排序的空间复杂度

在冒泡排序中,为了给传给我们的数组排序而额外创建的变量就只有end,exchange,i等等,也就是常数个,因此冒泡排序的空间复杂度是O(1)

创建了n+1个变量,因此空间复杂度是n+1,写作O(n+1),可能疑问就是这不是开辟了能放n+1个long long类型的空间吗,怎么就开辟变量了,这虽然是个指针,指针变量的名字实际上就相当于一个数组名,申请空间相当于是创建了一个数组,我可以使用[]操作符来访问这块空间的元素,比如fibArray[1]就访问了第二个元素,这不就是创建变量吗?

实际上我们在求空间复杂度的时候没有必要每次都紧扣定义创建的变量个数,一般来说就是关注开辟的空间能放多少个变量。

Fac(N)会调用Fac(N-1)以此类对直到Fac(1)调用Fac(0),一共调用了N+1次,每次里面创建了常数个变量,因此空间复杂度是O(N)

在上面计算过Fib的时间复杂度是O(2^N),也就是说执行了这个量级次基本操作,而每次基本操作创建的变量都是常数个,那是不是说Fib的空间复杂度也是O(2^N)呢?首先说答案:不是。在计算时间复杂度和空间复杂度的时候,我们应该遵循时间是一去不复返的,空间是可以重复利用的。

什么叫空间可以重复利用呢?

首先来说重复利用并不是共用,因为在调用完某个函数的时候,为他申请的栈帧就随之销毁了,何谈共用?

比如有这样一段代码

我们发现这两个变量的地址是相同的,这是因为我们在主函数里面调用了这两个函数,先调用Func1,于是为他开辟了一块栈帧,里面有变量a

Func1调用完成之后,为他开辟的栈帧就被换给了操作系统,俗称栈帧被销毁,然后就开始调用Func2,也要为他开辟一块栈帧,前面通过打印地址我们知道这两个变量的地址是相同的,也就是说为Func2开辟的栈帧也是同一块。这就是空间的重复利用。

再回到Fib函数的空间复杂度计算

假设我们刚上来调用的是Fib(5)

计算这种递归函数的空间复杂度主要就是考虑开辟的栈帧是什么情况,比如我们刚上来调用的是Fib(5),Fib(5)为了得到返回值,就要调用Fib(4)和Fib(3),那问题来了,先后给Fib(4)和Fib(3)开辟栈帧吗?当然不是,先调用的是Fib(4),Fib(4)都还没返回呢,那必然不能调用Fib(3),那就不能为Fib(3)开辟栈帧,而是继续执行Fib(4),而Fib(4)会调用Fib(3)和Fib(2),先调用的是Fib(3),Fib(3)为了得到返回值就会调用Fib(2)和Fib(1),先调用的是Fib(2),此时有返回值了,在返回之后为Fib(2)申请的这快栈帧就可以还给操作系统了,有了返回值Fib(2)就算执行完了,那就可以接着调用Fib(1),从而又要为Fib(1)开辟一块栈帧,这块栈帧和刚才Fib(2)申请的那块是同一块,也能得到一个返回值并把这块栈帧销毁,这样Fib(3)就有值了,就能调用倒数第二行的Fib(2)了,于是为他申请一块栈帧,这快栈帧和刚才调用完Fib(3)被销毁的那块是同一块栈帧,以此类推,最终实际上就只申请了4块栈帧。

于是调用Fib(N)就申请了N-1块栈帧,因为同层的都在重复利用同一块空间,因此Fib的空间复杂度是O(N)。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WebGL中开发科学数据可视化应用

WebGL在科学数据可视化领域有广泛的应用,可以用于呈现和解释复杂的科学数据。以下是在WebGL中开发科学数据可视化应用时的一些建议,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 1.选择合…

网络原理 - HTTP/HTTPS(4)

HTTP响应详解 认识"状态码"(status code) 状态码表示访问一个页面的结果.(是访问成功,还是失败,还是其它的一些情况...).(响应结果如何) 学习状态码 -> 为了调试问题. 写服务器时,按照状态码的含义正确使用. 200 OK 这是最常见的状态码,表示访问成功. 抓包抓…

Android加载富文本

直接用webview加载: package com.example.testcsdnproject;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.webk…

docker (十一)-进阶篇-docker-compos最佳实践部署zabbix

一 部署docker环境 关闭防火墙、selinux、开启docker,并设置开机自启动 注意点:docker部署的时候,bip要指定,不然会导致虚拟机ip和容器ip冲突,ssh连不上虚拟机 部署请参考 docker (二)-yum…

数据库管理-第153期 Oracle Vector DB AI-05(20240221)

数据库管理153期 2024-02-21 数据库管理-第153期 Oracle Vector DB & AI-05(20240221)1 Oracle Vector的其他特性示例1:示例2 2 简单使用Oracle Vector环境创建包含Vector数据类型的表插入向量数据 总结 数据库管理-第153期 Oracle Vecto…

计算机服务器中了devos勒索病毒怎么办?Devos勒索病毒解密数据恢复

网络技术的不断发展与更新,为企业的生产运营提供了有利保障,企业的生产运营离不开数据支撑,通过企业数据可以综合调整发展运营方向,但网络是一把双刃剑,近期,云天数据恢复中心接到许多企业的求助&#xff0…

2.20 Qt day1

一. 思维导图 二. 消化常用类的使用&#xff0c;以及常用成员函数对应的功能 按钮类QPushButton&#xff1a; mywidget.h&#xff1a; #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget> #include<QPushButton>//按钮类 #include<QIcon>class MyW…

ts快速入门

文章目录 一、运行环境1、线上Playground2、VSCode 编辑器3、Code Runner 插件4、ts-node 二、声明1、变量声明2、常量声明3、类型推断 三、常用数据类型1、number2、string3、boolean4、数组5、对象 四、函数1、函数声明语法2、参数详解&#xff08;1&#xff09;特殊语法&…

C++学习Day08之类模板碰到继承的问题以及解决

目录 一、程序及输出1.1 指定父类T数据类型1.2 子类T指定父类T数据类型 二、分析与总结 一、程序及输出 1.1 指定父类T数据类型 必须要指定出父类中的T数据类型&#xff0c;才能给子类分配内存 正确使用 &#xff1a; #include<iostream> using namespace std;templa…

webpack打包速度优化思维导图

webpack打包速度优化思维导图 前言附件 前言 去年的时候公司一个项目体积过大&#xff0c;我是m1芯片的macpro&#xff0c;光启动就要1分钟&#xff0c;配置差点都电脑&#xff0c;启动就要3分钟&#xff0c;自然打包速度也会慢很多&#xff0c;我们是gitlab设置成了自动打包的…

春招面试准备笔记——NMS(非极大值抑制)算法

NMS&#xff08;非极大值抑制&#xff09;算法非极大值抑制是用于减少物体检测算法中重叠边界框或区域的数量的技术。通过对每个类别的检测框按置信度排序&#xff0c;然后逐个遍历&#xff0c;保留置信度最高的框&#xff0c;并抑制与其重叠且置信度低的框&#xff0c;从而得到…

Apache Httpd 常见漏洞解析(全)

一、Apache HTTPD 换行解析漏洞 漏洞编号&#xff1a;CVE-2017-15715 Apache HTTPD是一款HTTP服务器&#xff0c;它可以通过mod_php来运行PHP网页。 其2.4.0~2.4.29版本中存在一个解析漏洞。 在解析PHP时&#xff0c;1.php\x0A将被按照PHP后缀进行解析&#xff0c;导致绕过…

桌面备忘录怎么设置,怎么在电脑上设置提醒?

桌面备忘录怎么设置&#xff0c;怎么在电脑上设置提醒&#xff1f;如今&#xff0c;为了优质生活的需要&#xff0c;我们每天都会处理很多的事情&#xff0c;比如&#xff1a;网课时间、工作任务、生活琐事……有时候&#xff0c;真恨不得自己有个三头六臂&#xff0c;好把这一…

前端|JavaScript 基础 - 第2天(黑马笔记)

JavaScript 基础 - 第2天 目录 JavaScript 基础 - 第2天一、运算符1.算术运算符2.赋值运算符3.自增/自减运算符4.比较运算符5.逻辑运算符6.运算符优先级 二、语句1.表达式和语句2.分支语句if 分支语句if双分支语句if 多分支语句三元运算符&#xff08;三元表达式&#xff09;sw…

分治算法总结(Java)

目录 分治算法概述 快速排序 练习1&#xff1a;排序数组 练习2&#xff1a;数组中的第K个最大元素 练习3&#xff1a;最小k个数 归并排序 练习4&#xff1a;排序数组 练习5&#xff1a;交易逆序对的总数 练习6&#xff1a;计算右侧小于当前元素的个数 练习7&#xff1…

视频推拉流EasyDSS视频直播点播平台授权出现激活码无效并报错400是什么原因?

视频推拉流EasyDSS视频直播点播平台集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体&#xff0c;可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务&#xff0c;在应用场景上&#xff0c;平台可以运用在互联网教育、在线课堂、游戏…

Project_Euler-06 题解

Project_Euler-06 题解 题目描述 两个公式 等差数列求和公式 i i i项&#xff1a; a i a_{i} ai​ 项数&#xff1a; n n n 公差&#xff1a; d d d 和&#xff1a; S n S_{n} Sn​ a n a 1 ( n − 1 ) d S n n ( a 1 a n ) 2 a_{n} a_{1} (n - 1)d\\ S_{n} \frac{n(a_…

在哪些领域中最需要使用 OCR 识别技术?真实场景介绍

根据我们的项目经验总结来说&#xff0c;OCR&#xff08;光学字符识别&#xff09;技术在多个领域中扮演着至关重要的角色&#xff0c;它能够将图像中的文本内容转换为机器可读的格式&#xff0c;极大地提高了数据处理的效率和准确性。以下是一些主要领域及其对应的应用场景和用…

ubuntu22.04@laptop OpenCV Get Started: 012_mouse_and_trackbar

ubuntu22.04laptop OpenCV Get Started: 012_mouse_and_trackbar 1. 源由2. mouse/trackbar应用Demo2.1 C应用Demo2.2 Python应用Demo 3. 鼠标位置跟踪注释3.1 注册回调函数3.2 回调操作3.3 效果 4. 使用轨迹栏调整图像大小4.1 初始化轨迹栏&注册回调函数4.2 回调操作4.3 效…

每期十万,等你来战|AI原生应用开发挑战赛首期精彩回顾

随着大模型技术的飞速发展&#xff0c;2024年将会成为AI原生应用爆发的元年&#xff0c;引领千行百业的创新变革。在这一时代背景下&#xff0c;百度智能云重磅推出千帆杯AI原生应用开发挑战赛&#xff0c;旨在激发广大开发者的创意潜能&#xff0c;推动AI原生应用在中国市场的…