当前位置: 首页 > news >正文

空间复杂度(超详解+例题)

全文目录

  • 引言
  • 空间复杂度
  • 例题
  • 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&#xff08;冒泡排序&#xff09;test3&#xff08;求阶乘&#xff09;test4&#xff08;斐波那契数列&#xff09;总结引言 在上一篇文章中&#xff0c;我们提到判断一个算法的好坏的标准是时间复杂度与空间复杂度。 时间复杂度的作用…...

Document-Level event Extraction via human-like reading process 论文解读

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

H5盲盒抽奖系统源码

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

低代码平台和无代码平台哪个更适合开发企业管理系统?

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

75岁彪马再发NFT 复活美洲狮IP

在“运动品牌Web3”的潮流里&#xff0c;彪马&#xff08;PUMA&#xff09;绝对算是发烧友级别。2月22日&#xff0c;这家德国服装品牌的新NFT又来了&#xff0c;总量10000个Super PUMA NFT中&#xff0c;将有4000个以0.15 ETH&#xff08;约为255美元&#xff09;价格正式公售…...

大学生成人插画培训机构盘点

成人插画培训机构哪个好&#xff0c;成人学插画如何选培训班&#xff1f;给大家梳理了国内较好的插画培训机构排名&#xff0c;各有优势和特色&#xff0c;供大家参考&#xff01; 一&#xff1a;国内成人插画培训机构排名 1、轻微课&#xff08;五颗星&#xff09; 主打课程有…...

【算法基础】一维差分 + 二维差分

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;【C/C】算法 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有…...

游戏服务器框架 技能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 的认识&#xff0c;大致分为下面几个主题&#xff0c;Socket 是什么&#xff0c;Socket 是如何创建的&#xff0c;Socket 是如何连接并收发数据的&#xff0c;Socket 套接字的删除等。 Socket 是什么以及创建过程 一个数据包经由应用程序产生&#xff0c;进入到…...

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 是互联网技术领域使用最为广泛的存储中间件&#xff0c;它是「Remote Dictionary Service」的首字母缩写&#xff0c;也就是「远程字典服务」。基于内存存储&#xff0c;读写性能高适合存储热点数据&am…...

【Linux】编辑器——vim(最小集+指令集+自动化配置)

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

Centos7+Xshell+Jenkins堆装

windows系统崩坏&#xff0c;重装测试类工具&#xff0c;心情崩了 windows硬盘损坏前&#xff0c;运行应用具慢。。。。。。慢着慢着就走了 从前部署在本地的jenkins&#xff0c;python&#xff0c;gitblit等相关脚本都凉透了&#xff0c;所以这次把服务部署到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上实现一些需求&#xff0c;进行记录一下&#xff0c;关于进程保活的基础知识可以参考Android system — 进程生命周期与ADJ&#…...

oracle表 分组,并查每组第一条

oracle主要用到的函数:OVER(PARTITION BY) mysql主要用到的函数:LIMIT &#xff08;用到3个地方&#xff1a;分组列、组内排序列、表名&#xff09; oracle&#xff1a; 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>元素

简单介绍&#xff1a; 在之前我们做的学生管理系统的时候&#xff0c;曾经有一个环节是修改学生的数据。我们在修改的时候是必须将student对象的三个属性全部填入信息&#xff0c;然后全部修改才可以&#xff0c;这样会造成一个问题就是在我们明明只需要修改一个属性的时候却要…...

【极海APM32替代笔记】低功耗模式配置及配置汇总

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

攻击者失手,自己杀死了僵尸网络 KmsdBot

此前&#xff0c;Akamai 的安全研究员披露了 KmsdBot 僵尸网络&#xff0c;该僵尸网络主要通过 SSH 爆破与弱口令进行传播。在对该僵尸网络的持续跟踪中&#xff0c;研究人员发现了一些有趣的事情。 C&C 控制 对恶意活动来说&#xff0c;最致命的就是夺取对 C&C 服务…...

东阿县高新技术企业认定条件和优惠政策 山东同邦科技分享

东阿县高新技术企业认定条件和优惠政策 山东同邦科技分享 高新技术企业 在《国家重点支持的高新技术领域》内&#xff0c;持续进行研究开发与技术成果转化&#xff0c;形成企业核心自主知识产权&#xff0c;并以此为基础开展经营活动&#xff0c;在中国境内&#xff08;不包…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...