news 2026/5/9 13:03:01

数据结构空间复杂度

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据结构空间复杂度

空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 ,空间复杂度不是程序占用了多少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)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 1:24:43

自定义分配器实战

1、非修改序列算法这些算法不会改变它们所操作的容器中的元素。1.1 find 和 find_iffind(begin, end, value)&#xff1a;查找第一个等于 value 的元素&#xff0c;返回迭代器&#xff08;未找到返回 end&#xff09;。find_if(begin, end, predicate)&#xff1a;查找第一个满…

作者头像 李华
网站建设 2026/5/9 1:24:42

谷歌联合打击全球最大住宅代理网络IPIDEA

谷歌周三宣布&#xff0c;其与合作伙伴联手成功打击了IPIDEA——这个被描述为世界上最大的住宅代理网络之一的组织。 为此&#xff0c;谷歌表示采取了法律行动&#xff0c;关闭了数十个用于控制设备和通过这些设备代理流量的域名。截至发稿时&#xff0c;IPIDEA的官方网站&…

作者头像 李华
网站建设 2026/5/8 4:01:26

为啥大厂 FPS 进对局不立刻清空局外缓存,而是打完再清?(大白话超长版)

玩 FPS 你肯定见过这种流程: 你在大厅里:看商城、换枪皮、调灵敏度、看好友在线 点“开始匹配” 进加载界面 进对局:打得热火朝天 打完结算:经验+1、段位涨、任务完成、掉落发放 回大厅:又能立刻点下一把、又能立刻看战绩、聊天也还在 很多人做框架时会冒出一个“很合理但…

作者头像 李华
网站建设 2026/5/9 8:53:16

【课程设计/毕业设计】基于微信小程序的医院设备管理及报修系统基于springboot的医院设备管理及报修小程序的设计与实现【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/8 4:01:41

Java毕设项目推荐-基于springboot+vue智慧旅游服务平台网站系统基于springboot的智慧旅游系统【附源码+文档,调试定制服务】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华