空间复杂度(超详解+例题)
全文目录
- 引言
- 空间复杂度
- 例题
- test1
- test2(冒泡排序)
- test3(求阶乘)
- test4(斐波那契数列)
- 总结
引言
在上一篇文章中,我们提到判断一个算法的好坏的标准是时间复杂度与空间复杂度。
时间复杂度的作用是衡量一个算法运行的快慢;而空间复杂度是衡量一个算法运行所需的额外的空间。在上一篇文章中,我们已经了解了计算时间复杂度的方法,以及如何使用大O阶表示法来表示时间复杂度的大概值。
戳我转到时间复杂度详解哦
在本篇文章中将详细介绍关于空间复杂度的相关内容:
空间复杂度
空间复杂度也是一个数学表达式,是对一个算法在运行过程中所额外占用的空间大小的量度。
在时间复杂度中,我们以算法中基本语句执行的次数作为算法的时间复杂度;
而空间复杂度中,我们以变量被创建的个数作为算法的空间复杂度(不论是什么类型的变量,都算做一个空间复杂度)。
需要注意的是:由于函数运行时需要的栈空间在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定(参数等一些数据不必计算)。
我们在计算空间复杂度时,依旧采用大O阶表示法来确定其大概值。
转换大O阶表示的方式与时间复杂度相同:
1、用常数1表示算法中的所有加法常数;
2、在修改后的数学表达式中只保留最高项;
3、如果最高项存在且不是1,则去掉该最高项的系数。
但是,通常情况下,我们是不需要将精确的空间复杂度计算出来后再转换的。与时间复杂度相同,空间复杂度的大O阶表示法也分为例如对数级(log n)、正比例级(n)、次方级(n^2)、指数级(2^n)等:
但是对于空间复杂度而言,最常见的就是O(1)与O(n),其他量级就不是很常见。
例题
接下来就通过几个栗子来理解空间复杂度:
test1
int* rotate(int* nums, int numsSize, int k)
{int* ret = (int*)calloc(numsSize, sizeof(int));int i = 0;for (i = numsSize-k; i < numsSize ; i++){*ret++ = nums[i];}for (i = 0; i < numsSize - k; i++){*ret++ = nums[i];}return ret-numsSize;
}
在这段算法中,我们实现了将数组的后k个元素移动到数组的前面。
在计算这个算法的空间复杂度时,数组nums、整型numsSize与k均为参数,所以不计算空间复杂度。在实现元素的移动时,我们动态开辟了一块空间,这块动态空间由numsSize个整型,所以这个算法的空间复杂度就是O(n)。
不难发现,在算法中还创建了一个整型变量i,但是,这个常数就省略不计了。
test2(冒泡排序)
void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}
这段算法是经典的冒泡排序。
在段算法中,额外申请了三个整型变量end、exchange、i 。空间复杂度是常数,大O表示法中常数忽略后表示为O(1)。
test3(求阶乘)
long long Fac(size_t N)
{if (N == 0)return 1;return Fac(N - 1) * N;
}
此算法可以实现求N的阶乘。
在这个算法中,我们通过函数递归的方式,每次递归将N-1作为参数,返回Fac(N - 1) 与 N的乘积。
参数为0时,递归终止,开始返回值。
这里需要注意的是,函数在栈中开辟函数栈帧时是依次开辟的。当函数调用它本身时,会在栈中再开辟一块空间作为新的函数的栈帧。在每一块栈帧中,并没有创建额外的变量,所以我们可以认为每一块栈帧的空间复杂度是常数个。所以,有多少次递归,就会有多少个函数的栈帧。
我们可以画图来表示在这个算法中栈帧的开辟:
从参数为N递归到参数为0,共递归了N+1次。省略常数1,该算法的空间复杂度就是O(n)。
test4(斐波那契数列)
long long Fib(size_t N)
{if (N < 3)return 1;return Fib(N - 1) + Fib(N - 2);
}
此算法通过递归实现计算第n个斐波那契数。
在上一篇文章中,我们介绍了这个算法的时间复杂度的计算,结果是O(2^n)。通过计算时间复杂度,我们了解了这个算法的递归规律:
根据上一个例题,我们知道,在这种没有新建变量的递归中,每次递归的空间复杂度都可以看作常数。大概是递归了2^n次,那空间复杂度也是O(2^n)吗?
其实并不是,其实这个算法的空间复杂度是O(n)。
函数调用时会为函数开辟一块栈帧,当函数调用结束后,这块空间就会被还给操作系统。这块空间是可以重复利用的。比如在调用某函数结束后,再次调用相同的函数时,所用的空间与上次调用完的空间是相同的,这里举一个小栗子:
void Fun1()
{int i = 10;printf("%p\n", &i);
}
void Fun2()
{int j = 10;printf("%p\n", &j);
}
int main()
{Fun1();Fun2();return 0;
}
这段代码中,main函数调用了两个相同的函数Fun1与Fun2。在这两个函数中都创建了一个整型的变量。我们打印这两个变量的地址,发现它们是相等的。这就说明这两个函数所使用的是同一块空间。
再回到斐波那契数列。这个算法的递归并不是我们想象的一次递归同时开辟两个函数栈帧,而是先在一条线上递归到终止后返回一个值,释放此次递归的空间,再回到上一级的递归,然后再到终止后返回一个值,再释放此次的空间,然后再回到上一级递归,释放空间。最终,将所有的返回值汇到最初的函数中,得到最终的结果:
这张图示应该可以比较清楚地描述该算法的递归:
首先顺着第一条线递归,直到小于3时终止。到此,函数一共递归N-1次,空间复杂度为O(n)。返回1后,为Fib(2)开辟的栈帧被释放;下一步调用Fib(1),并为其开辟空间,这时开辟的空间与刚才Fib(2)的空间是同一块该函数的参数也小于3,递归终止,返回1,为Fib(1)开辟的栈帧被释放。此时,Fib(N-4)的返回值就可以被计算出来,即Fib(2)与Fib(1)的和(假设N-4-1的值为2),返回后,为Fib(N-4)开辟的空间也被释放;接下来为Fib(N-5)开辟栈帧,该空间与刚才释放的Fib(N-4)的函数栈帧是同一块空间…
依次类推,其实该算法开辟的空间就只有最开始第一条线递归时所开辟的空间,后面的递归开辟空间时使用的都是之前释放的空间。所以,该算法的空间复杂度就是O(n)。
通过这个斐波那契数列的递归算法,我们不难发现:
时间是一去不复返的,在运行基本语句时,势必要消耗时间;
而空间是可以重复利用的,我们在使用完一块空间后,该空间被释放后是可以再次利用的。
所以在许多的算法中,会使用空间换时间的思想,尽量先保证时间复杂度的减少。
总结
在本篇文章中,我们了解了空间复杂度的相关知识,以及能够计算算法的空间复杂度
到此,对于算法效率的判断的两个标准空间复杂度与时间复杂度都已经介绍完了
如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出
如果本文对你有帮助,希望一键三连哦
希望与大家共同进步哦
相关文章:

空间复杂度(超详解+例题)
全文目录引言空间复杂度例题test1test2(冒泡排序)test3(求阶乘)test4(斐波那契数列)总结引言 在上一篇文章中,我们提到判断一个算法的好坏的标准是时间复杂度与空间复杂度。 时间复杂度的作用…...

Document-Level event Extraction via human-like reading process 论文解读
Document-Level event Extraction via human-like reading process 论文:2202.03092v1.pdf (arxiv.org) 代码:无 期刊/会议:ICASSP 2022 摘要 文档级事件抽取(DEE)特别困难,因为它提出了两个挑战:论元分散和多事件。第一个挑战…...

H5盲盒抽奖系统源码
盲盒抽奖系统4.0,带推广二维码防洪炮灰功能和教程。 支持微信无限回调登录 标价就是源码价格,vuetp5框架编写,H5网页,前后端分离 此源码为正规开发,正版产品已申请软著。 开源无加密无授权,可以二开使用…...

低代码平台和无代码平台哪个更适合开发企业管理系统?
编者按:本文分析了开发企业管理系统所需要的平台特性,并根据这些特点和低代码无代码的优劣比较,得出低代码平台更适合开发企业管理系统。关键词:私有化部署,可视化设计,源码交付,数据集成&#…...

75岁彪马再发NFT 复活美洲狮IP
在“运动品牌Web3”的潮流里,彪马(PUMA)绝对算是发烧友级别。2月22日,这家德国服装品牌的新NFT又来了,总量10000个Super PUMA NFT中,将有4000个以0.15 ETH(约为255美元)价格正式公售…...

大学生成人插画培训机构盘点
成人插画培训机构哪个好,成人学插画如何选培训班?给大家梳理了国内较好的插画培训机构排名,各有优势和特色,供大家参考! 一:国内成人插画培训机构排名 1、轻微课(五颗星) 主打课程有…...

【算法基础】一维差分 + 二维差分
👦个人主页:Weraphael ✍🏻作者简介:目前正在学习c和算法 ✈️专栏:【C/C】算法 🐋 希望大家多多支持,咱一起进步!😁 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有…...
游戏服务器框架 技能buff篇
游戏服务器框架 技能buff篇 1.状态 state 全局API 用于定义各种状态检查 bool IsDead(){ // 死亡buff if (buff->id 10001){ return true; } return false; } bool IsInvincible(){ if (buff->id 20001 || buff->id 20002){…...

网友说socket通信讲的不彻底,原来这才是Socket
关于对 Socket 的认识,大致分为下面几个主题,Socket 是什么,Socket 是如何创建的,Socket 是如何连接并收发数据的,Socket 套接字的删除等。 Socket 是什么以及创建过程 一个数据包经由应用程序产生,进入到…...

Nginx第二讲
目录 二、Nginx02 2.1 keepalived和heartbeat介绍 2.1.1 两者的介绍 2.1.2 keepalived简介 2.1.3 VRRP协议与工作原理 2.1.4 Keepalvied的工作原理 2.2 安装环境及keepalived 2.3 启动与验证keepalived 2.4 keepalived测试 2.4.1 环境准备 2.4.2 配置keepalived 2.…...
redis(win版)
1. 前言1.1 什么是RedisRedis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件,它是「Remote Dictionary Service」的首字母缩写,也就是「远程字典服务」。基于内存存储,读写性能高适合存储热点数据&am…...

【Linux】编辑器——vim(最小集+指令集+自动化配置)
目录 1.vim最小集 1.1 vim的三种模式 1.2 vim的基本操作 2.vim指令集 2.1 命令模式指令集 移动光标 删除文字 复制 替换 撤销上一次操作 更改 跳至指定的行 2.2 底行模式指令集 列出行号 跳到文件中的某一行 查找字符 保存文件 多文件操作 3.如何配置vim 配…...

Centos7+Xshell+Jenkins堆装
windows系统崩坏,重装测试类工具,心情崩了 windows硬盘损坏前,运行应用具慢。。。。。。慢着慢着就走了 从前部署在本地的jenkins,python,gitblit等相关脚本都凉透了,所以这次把服务部署到Centos7上…...

Android system实战 — Android R(11) 进程保活白名单
Android system实战 — Android R 进程保活白名单0. 前言1. 具体实现1.1 准备工作1.2 源码实现1.2.1 源码1.2.2 diff文件0. 前言 最近在Android R上实现一些需求,进行记录一下,关于进程保活的基础知识可以参考Android system — 进程生命周期与ADJ&#…...
oracle表 分组,并查每组第一条
oracle主要用到的函数:OVER(PARTITION BY) mysql主要用到的函数:LIMIT (用到3个地方:分组列、组内排序列、表名) oracle: select t.* from ( select a.*, ROW_NUMBER() OVER (PARTITION BY 分组列 ORDER BY 组内排…...
Java代码弱点与修复之——DE: Dropped or ignored exception(无视或忽略异常)
弱点描述 Dropped or ignored exception(DE)指的是在代码中抛出的异常被捕获后被无视或忽略了,而不是被适当地处理。这种情况通常发生在程序员没有处理异常或处理异常时不小心忽略了异常的情况下。 Dropped or ignored exception会导致程序无法正常工作,因为异常会阻塞程…...

JavaEE简单示例——动态SQL之更新操作<set>元素
简单介绍: 在之前我们做的学生管理系统的时候,曾经有一个环节是修改学生的数据。我们在修改的时候是必须将student对象的三个属性全部填入信息,然后全部修改才可以,这样会造成一个问题就是在我们明明只需要修改一个属性的时候却要…...

【极海APM32替代笔记】低功耗模式配置及配置汇总
【极海APM32替代笔记】低功耗模式配置及配置汇总 文章总结:(后续更新以相关文章为准) 【STM32笔记】低功耗模式、WFI命令等进入不了休眠的可能原因(系统定时器SysTick一直产生中断) 【STM32笔记】HAL库低功耗模式配置…...

攻击者失手,自己杀死了僵尸网络 KmsdBot
此前,Akamai 的安全研究员披露了 KmsdBot 僵尸网络,该僵尸网络主要通过 SSH 爆破与弱口令进行传播。在对该僵尸网络的持续跟踪中,研究人员发现了一些有趣的事情。 C&C 控制 对恶意活动来说,最致命的就是夺取对 C&C 服务…...
东阿县高新技术企业认定条件和优惠政策 山东同邦科技分享
东阿县高新技术企业认定条件和优惠政策 山东同邦科技分享 高新技术企业 在《国家重点支持的高新技术领域》内,持续进行研究开发与技术成果转化,形成企业核心自主知识产权,并以此为基础开展经营活动,在中国境内(不包…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...