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

数据结构空间复杂度

空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 ,空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数,空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法, 注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定

int ArraySum(int* a, size_t n) { assert(a); int sum = 0; for (size_t i = 0; i < n; ++i) { sum += a[i]; } return sum; }

函数中开辟的额外空间仅为2个常数级临时变量:整型变量sum、循环变量i;

无动态内存申请(无malloc/calloc)、无递归调用(无栈帧开销),所有额外空间的大小与数据规模n无关,固定不变;

按规则,常数级的额外空间复杂度为O(1)

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; } }

逐一看代码中开辟的辅助变量/空间,判断其是否与n相关:

循环变量:size_t/end、size_t、i——都是单个整型变量,数量固定,与n无关;

优化标记:int exchange——单个整型变量,用于标记本轮是否发生交换,数量固定,与n无关;

交换函数Swap:冒泡排序的交换逻辑是原地交换(直接操作原数组的两个元素),无论Swap是用临时变量实现(int temp=*a;*a=*b;*b=temp;)还是异或实现,都只用到单个临时变量,依然是常数级空间,与n无关。

关键结论:这段代码中所有额外开辟的辅助空间,都是固定的常数级变量,没有动态申请数组、链表等随n变化的空间,也无递归栈开销

因此,这段冒泡排序的空间复杂度为 O(1),冒泡排序也属于原地排序算法

long long* Fibonacci(size_t n) { if(n==0) return NULL; long long * fibArray = (long long *)malloc((n+1) * sizeof(long long)); fibArray[0] = 0; fibArray[1] = 1; for (int i = 2; i <= n ; ++i) { fibArray[i] = fibArray[i - 1] + fibArray [i - 2]; } return fibArray; }

先逐一看代码中额外开辟的所有空间,区分结果存储空间辅助空间

动态数组fibArray(结果存储空间)

函数通过malloc动态开辟了(n+1)个long long类型的连续空间,用于存储前n+1个斐波那契数(fibArray[0]~fibArray[n]),这个空间的大小随n线性增长,且是输出结果的必要空间——函数要返回所有斐波那契数,必须开辟该空间,无法用常数空间替代。

辅助变量

代码中仅用到循环变量int i这一个额外的临时变量,还有形参size_t n,都是单个整型/无符号整型变量,数量固定、占用空间恒定,与n无关,属于常数级辅助空间

无其他开销:迭代实现无递归栈帧开销,无额外动态内存申请,所有操作都是原地计算并存入数组

统计函数实际额外开辟的总空间(包含结果存储的动态数组)——这是开发中关注的重点,因为调用者使用该函数时,malloc的空间是函数主动开辟的额外内存,需要后续手动释放,

此时总额外空间随n线性增长,空间复杂度为O(n)(这是这个函数的核心空间复杂度结论)

辅助空间(排除输出结果的必要存储空间)——算法分析中若聚焦计算逻辑本身的空间开销,会忽略必须的结果存储。此时纯辅助空间只有常数级的循环变量i,空间复杂度为O(1)

long long Fac(size_t N) { if(N == 0) return 1; return Fac(N-1)*N; }

调用Fac(N)会触发线性链式的递归调用,直到Fac(0),完整的调用链是:

Fac(N)→Fac(N-1)→Fac(N-2)→...→Fac(1)→Fac(0)

从Fac(N)到Fac(0),逐层开辟栈帧,共开辟N+1个栈帧(包含基线条件Fac(0)的栈帧);

触发Fac(0)返回1后,才会从Fac(0)到Fac(N)逐层释放栈帧,栈帧不会提前释放。

2.推导空间复杂度

栈帧的最大数量:N+1(调用栈的深度为N+1);

单个栈帧的空间复杂度:O(1)(仅保存形参N、返回地址等,固定大小,与N无关);

总空间复杂度=栈帧最大数量×单个栈帧空间=(N+1)×O(1)

根据空间复杂度的规则,最终空间复杂度为O(N)

long long Fib(size_t N) { if (N < 3) return 1; return Fib(N-1) + Fib(N-2); }

空间复杂度由调用栈的最大深度决定,与总递归调用次数无关

程序采用深度优先遍历执行递归:先将Fib(N-1)的调用栈执行到底(直到Fib(2)/Fib(1)),再回溯计算Fib(N-2),栈帧会复用,不会同时开辟所有调用的栈帧

调用栈的最大深度为N-1(从Fib(N)到Fib(1)的链式深度),每个栈帧为常数级O(1)空间;

总空间开销 = 栈帧最大深度 × 单个栈帧空间 = (N−1)×O(1),忽略常数项,故为O(N)

int** CreateMatrix(size_t n) { if (n == 0) return NULL; int** mat = (int**)malloc(n * sizeof(int*)); for (size_t i = 0; i < n; ++i) { mat[i] = (int*)malloc(n * sizeof(int)); } return mat; }

函数动态开辟了n阶方阵的连续空间,总开辟的内存单元数为:行指针数组n个+每行n个 int × n行=n+n^2;

按空间复杂度规则,保留最高阶项,忽略低阶项和常数项,n2是最高阶项,主导空间增长趋势;

额外临时变量仅循环变量i(常数级O(1)),对整体复杂度无影响;空间复杂度为O(n2)

int* ReverseArray1(int* a, size_t n) { if (n == 0) return NULL; int* ret = (int*)malloc(n * sizeof(int)); for (size_t i = 0; i < n; ++i) { ret[i] = a[n-1-i]; } return ret; } void ReverseArray2(int* a, size_t n) { assert(a); size_t left = 0, right = n-1; while (left < right) { Swap(&a[left], &a[right]); left++; right--; } }

非原地反转

动态开辟了大小为n的int类型新数组ret,用于存储反转后的结果,该空间随n线性增长;

额外临时变量仅循环变量i(常数级O(1))

总空间开销由动态数组主导,空间复杂度为O(1)

原地反转

开辟的额外空间仅为2个常数级临时变量:左指针left、右指针right

Swap交换(单个临时变量,常数级),无动态内存、无递归,所有操作直接在原数组上完成额外空间与n无关;空间复杂度为O(1)

http://www.jsqmd.com/news/318812/

相关文章:

  • 搜维尔科技:集成Tesollo DG-5F机械手的类人平台
  • 原来PixPin不止能截图!配置快捷键后,OCR和翻译也能一键搞定
  • 提示工程架构师带继任者的3大误区,第1个90%管理者都犯过
  • 自定义分配器实战
  • 【服务器】为安全考虑,已锁定该用户帐户,原因是登录尝试或密码更。改尝试过多。请稍候片刻再重试,或与系统管理员或技术支持联系。
  • 卷王必备!SpringBoot极简审批流:1行代码搞定请假系统,摸鱼时间翻倍
  • python安卓的热门短视频播放平台小程序
  • 4589126
  • python安卓客户端室内定位APP_jrate小程序
  • 衡石科技ChatBI战略解码:以Agentic BI内核,定义企业级对话式分析的未来
  • C++中的函数式编程
  • python安卓的校园生活信息服务APP小程序
  • 衡石科技实践:如何基于统一指标平台,实现从传统BI到Agentic BI的架构演进
  • 用Python监控系统日志并发送警报
  • PCIe-FC Information Tracked by Transmitter
  • HarmonyOS 游戏里的“假异步”,为什么会卡
  • AI大模型应用开发工程师:技术与产业的“翻译官“,月薪可达60k的热门职业
  • Java计算机毕设之基于java+springboot+vue+mysql的高校院系学生信息管理系统 基于springboot的高校院系学生信息管理系统(完整前后端代码+说明文档+LW,调试定制等)
  • Java计算机毕设之基于java+springboot+vue+mysql的高校院系学生信息管理系统 基于springboot的高校院系学生信息管理系统(完整前后端代码+说明文档+LW,调试定制等)
  • 实用指南:Java Spring日志
  • 【大模型】-微调-BERT - 详解
  • 图神经网络传播优化新思路:ATP让大规模图学习更高效稳定
  • 智能体推理技术全解析:从CoT到多智能体协作的实战指南
  • Linux命令-lnstat(显示 Linux 网络统计信息)
  • Linux命令-lnstat(显示 Linux 网络统计信息)
  • Linux命令-ln(在文件或目录之间创建链接)
  • 鼠标放在图片上,图片3D倾斜
  • GUI by Python 6 一段 gui 代码分析
  • 0x3f 第46天 面向实习的八股背诵第三天 + 堆一题 很焦虑,感觉压根背不完,背了也不一定能讲出来,一直在想象面试的场景
  • 搜维尔科技:隆重推出MANUS Metagloves Pro Haptic触觉手套-精准的手部追踪与实时触觉反馈的完美结合