数据结构和算法学习记录——空间复杂度的计算(冒泡排序、阶乘递归、斐波那契数列递归、常见复杂度对比、栈帧、栈溢出)

简介: 数据结构和算法学习记录——空间复杂度的计算(冒泡排序、阶乘递归、斐波那契数列递归、常见复杂度对比、栈帧、栈溢出)

一、简单介绍

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

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

下面用实例来加深一下理解

二、冒泡排序的空间复杂度

2-1

//计算BullleSort的空间复杂度?
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;
    }
}

看上面所写的冒泡排序,我们要算额外创建的变量的个数。那么就是函数内部创建的变量(即不包括int*a,int n)


其中在第一个for循环中创建了第一个变量size_t end,第二个变量int exchange,嵌套的for循环里创建了第三个变量size_t i,共计三个变量。循环一轮结束,局部变量销毁,新一轮的循环创建的变量占用的是用一块空间,是重复的。


注:空间是可以重复利用,不累积的;时间是一去不复返,可以累积。


所以冒泡排序的额外变量个数为3个,即常数个。


故而冒泡排序的空间复杂度为:O(1)。

三、阶乘递归的空间复杂度

3-1

//计算Fac的空间复杂度?
long long Fac(size_t N)
{
    if (N == 1)
        return 1;
    else
        return Fac(N - 1) * N;
}

算递归的空间复杂度,主要要看递归的深度。一次调用Fac()时额外创建的变量是常数个,空间复杂度记为O(1)。

递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间,最终这个阶乘递归的空间复杂度为:O(N)。

栈帧

栈的概念: 在数据结构中, 栈是限定仅在表尾进行插入或删除操作的线性表。栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。

栈帧的概念:

每一次函数的调用,都会在调用栈(call stack)上维护一个独立的栈帧(stack frame).每个独立的栈帧一般包括:

1. 函数的返回地址和参数

2. 临时变量: 包括函数的非静态局部变量以及编译器自动生成的其他临时变量

3. 函数调用的上下文

四、斐波那契数列递归的空间复杂度

4-1

//计算斐波那契数列递归Fib的空间复杂度?
long long Fib(size_t N)
{
    if (N > 2)
        return Fib(N - 1) + Fib(N - 2);
    else
        return 1;
}

一开始可能会误以为答案是 ,但一想下来,如果空间复杂度为 的话,只要给出一个N = 50就会发生栈溢出了。显然是不合理的,在前面看冒泡排序的空间复杂度的时候讲过,空间是不累积的,使用过的空间依旧能再次被使用。

或者这么想,我们把Fib(N)到Fib(0)全都调用过一遍了,其余在调用他们的时候都是在Fib(N)到Fib(0)中重复的,而重复使用的是同一块空间。所以Fib()的空间复杂度为O(N)。

栈溢出

栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变。

五、常见复杂度对比(转载)

O(1) — 常数复杂度

O(log n) — 对数复杂度

O(n) — 线性复杂度

O(n log n) — 对数线性复杂度

O(nᵏ) — 多项式复杂度

O(kⁿ) — 指数复杂度

O(n!) — 阶乘复杂度

下图描绘了各种复杂度的算法中,当输入规模增长时,操作数量(运行时间)的变化趋势。

你可以看到,随着输入规模的增长,红色阴影区域中算法的运行时间急剧增长。另一方面,在黄色和绿色阴影区域中的算法,当输入规模增长时,运行时间在变化不是很大,因此它们更高效,处理大量数据时更游刃有余。


最后需要指明的一点,大 O 表示法通常用于描述当输入规模变得非常大时,算法呈现的「显著趋势」。因此,大的显著趋势会盖过一些小的细枝末节的趋势。例如,我们实际测算得到时间复杂度为 O(n²+ n) 的算法会简化为 O(n²),原因是随着 n 变得非常大时, n² 这一项的显著性远远盖过了 n 这一项的显著性。


目录
相关文章
|
8月前
|
算法 机器人
基于SOA海鸥优化算法的PID控制器最优控制参数计算matlab仿真
本课题研究基于海鸥优化算法(SOA)优化PID控制器参数的方法,通过MATLAB仿真对比传统PID控制效果。利用SOA算法优化PID的kp、ki、kd参数,以积分绝对误差(IAE)为适应度函数,提升系统响应速度与稳定性。仿真结果表明,SOA优化的PID控制器在阶跃响应和误差控制方面均优于传统方法,具有更快的收敛速度和更强的全局寻优能力,适用于复杂系统的参数整定。
|
算法 JavaScript 数据安全/隐私保护
基于GA遗传优化的最优阈值计算认知异构网络(CHN)能量检测算法matlab仿真
本内容介绍了一种基于GA遗传优化的阈值计算方法在认知异构网络(CHN)中的应用。通过Matlab2022a实现算法,完整代码含中文注释与操作视频。能量检测算法用于感知主用户信号,其性能依赖检测阈值。传统固定阈值方法易受噪声影响,而GA算法通过模拟生物进化,在复杂环境中自动优化阈值,提高频谱感知准确性,增强CHN的通信效率与资源利用率。预览效果无水印,核心程序部分展示,适合研究频谱感知与优化算法的学者参考。
|
算法 数据安全/隐私保护
基于Big-Bang-Big-Crunch(BBBC)算法的目标函数最小值计算matlab仿真
该程序基于Big-Bang-Big-Crunch (BBBC)算法,在MATLAB2022A中实现目标函数最小值的计算与仿真。通过模拟宇宙大爆炸和大收缩过程,算法在解空间中搜索最优解。程序初始化随机解集,经过扩张和收缩阶段逐步逼近全局最优解,并记录每次迭代的最佳适应度。最终输出最佳解及其对应的目标函数最小值,并绘制收敛曲线展示优化过程。 核心代码实现了主循环、粒子位置更新、适应度评估及最优解更新等功能。程序运行后无水印,提供清晰的结果展示。
320 14
【算法】栈
栈相关算法题,供参考,附有链接地址及板书
202 14
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
算法
[数据结构] -- 时间复杂度和空间复杂度
[数据结构] -- 时间复杂度和空间复杂度
190 0
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
399 59
|
11月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
229 0
栈区的非法访问导致的死循环(x64)
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
954 77
|
11月前
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。

热门文章

最新文章