08 算法评价标准:空间复杂度(大 O 渐进表示法、常见空间复杂度及案例分析、空间换时间)

embedded/2024/10/21 1:34:01/

目录

1 空间复杂度的理解

1.1 基本概念

1.2 大 O 渐进表示法

1.3 空间复杂度的意义

2 常见的空间复杂度

3 空间复杂度案例分析

3.1 常数级空间复杂度 O(1)

3.2 线性空间复杂度 O(n)

3.3 递归函数的空间复杂度

3.4 平方级空间复杂度 O(n^2)

空间复杂度时间复杂度的关系

4.1 时间复杂度 vs 空间复杂度

4.2 以空间换时间

4.3 空间复杂度的重要性

4.4 平衡时间和空间


1 空间复杂度的理解

1.1 基本概念

        空间复杂度是评估算法性能的一个重要指标,它描述的是算法在执行过程中所需额外存储空间的大小。这里的 “额外” 是指除了算法本身占用的存储空间(如源代码、常量、简单变量等)以及输入数据占用的存储空间之外的部分空间复杂度主要取决于算法中数据结构的使用情况,例如数组、链表、栈、队列等。通过分析空间复杂度,可以帮助我们了解算法在不同规模输入下对内存资源的需求,进而优化算法设计,提高程序的执行效率。

1.2 大 O 渐进表示法

        大 O 渐进表示法(Big O Notation)是一种数学工具,用于描述算法在输入规模趋向无穷大时,其时间和空间需求的增长趋势。在讨论空间复杂度时,大 O 渐进表示法帮助我们关注算法所需额外存储空间的增长率,而忽略掉那些在输入规模足够大时变得不重要的常数项和低阶项

        假设有一个算法,其所需额外空间 S(n) 是输入规模 n 的函数。如果存在两个正常数 c 和 n0,使得对于所有 n ≥ n0​ 都有 S(n) ≤ c⋅g(n),则称 S(n) 是 O(g(n))的,记作 S(n)=O(g(n))

这里:

  • S(n) 是算法的实际空间需求。
  • g(n)  是一个描述 S(n) 增长趋势的函数。
  • c 和 n0​ 是常数,用于确保不等式 S(n) ≤ c⋅g(n) 在 n ≥ n0​ 时成立。

1.3 空间复杂度的意义

        资源管理:了解算法的空间复杂度有助于合理分配和管理内存资源,尤其是在资源受限的环境中(如嵌入式系统、移动设备等),这一点尤为重要。

        性能优化:通过优化算法的空间复杂度,可以减少内存的使用,从而提高程序的运行效率。例如,减少不必要的数据复制、使用更高效的数据结构等。

        可扩展性:对于需要处理大规模数据的应用,低空间复杂度的算法更容易实现良好的可扩展性,能够在数据量增大时保持较高的性能。

        成本控制:在云计算等场景下,内存使用量直接影响到服务的成本。优化空间复杂度可以帮助降低运行成本。


2 常见的空间复杂度

常数级空间复杂度 O(1)

  • 如果一个算法在执行过程中所需的额外空间不随输入规模 n 的变化而变化,则称该算法具有常数级空间复杂度
  • 例如,一个简单的加法运算 int sum = a + b;,无论 a 和 b 的值如何,它所需的额外空间始终是一个固定大小的整数变量 sum,因此其空间复杂度为 O(1)。

线性空间复杂度 O(n)

  • 如果一个算法在执行过程中所需的额外空间与输入规模 n 成正比,则称该算法具有线性空间复杂度
  • 例如,一个算法需要为输入数组中的每个元素创建一个新的副本 int[] copy = new int[n];,则这个新数组的大小与原数组相同,因此其空间复杂度为 O(n)。

对数级空间复杂度 O(log⁡ n)

  • 如果一个算法在执行过程中所需的额外空间与输入规模 n 的对数成正比,则称该算法具有对数级空间复杂度
  • 例如,二分查找算法在每次迭代中将搜索范围减半,所需的额外空间主要用于存储中间变量,通常是对数级别的,因此其空间复杂度为 O(log ⁡n)。

平方级空间复杂度 O(n^2)

  • 如果一个算法在执行过程中所需的额外空间与输入规模 n 的平方成正比,则称该算法具有平方级空间复杂度
  • 例如,某些矩阵操作可能需要创建一个 n×n 的矩阵,因此其空间复杂度为 O(n^2)。

指数级空间复杂度 O(2^n)

  • 如果一个算法在执行过程中所需的额外空间与输入规模 n 的指数成正比,则称该算法具有指数级空间复杂度
  • 例如,某些递归算法或解决组合问题的算法可能需要存储大量的中间结果,导致空间复杂度呈指数增长,因此其空间复杂度为 O(2n)。

常见的空间复杂度关系:


3 空间复杂度案例分析

3.1 常数级空间复杂度 O(1)

        如果一个算法在执行过程中所需的额外空间不随输入规模 n 的变化而变化,则称该算法具有常数级空间复杂度 O(1)。这种算法也被称为原地工作算法或就地工作算法

void test1(int n)
{int i = 1;while (i <= n){i++;printf("%d", i);}
}

        在这个示例中,test1 函数的主要逻辑是通过一个 while 循环从 1 到 n 依次输出每个数字。我们来分析一下该函数的空间复杂度

  • 函数参数 n:作为函数的输入参数,n 占用 4 个字节。
  • 局部变量 i:在函数内部声明的局部变量 i 也占用 4 个字节。

        无论输入的 n 值多大,函数在执行过程中始终只需要这两个 int 类型的变量。假设一个 int 类型变量占用 4 个字节,那么整个函数所需的额外空间为:4 (for n) + 4 (for i) = 8 字节,因此,该函数的空间复杂度O(1),即常数级空间复杂度

3.2 线性空间复杂度 O(n)

        如果一个算法在执行过程中所需的额外空间与输入规模 n 成正比,则称该算法具有线性空间复杂度 O(n)。这意味着随着输入规模的增加,算法所需的额外空间也会按比例增加。

void test2(int n)
{int i;int arr[n];
}

        在这个示例中,test2 函数的主要逻辑是声明一个大小为 n 的动态数组 arr。我们来分析一下该函数的空间复杂度

  1. 函数参数 n:作为函数的输入参数,n 占用 4 个字节。
  2. 局部变量 i:在函数内部声明的局部变量 i 也占用 4 个字节。
  3. 动态数组 arr:数组 arr 的大小为 n,每个 int 类型的元素占用 4 个字节,因此数组 arr 占用 4n 个字节。

        综上所述,整个函数所需的额外空间为:4 (for n) + 4 (for i) + 4n (for arr) = 4n+8 字节,随着输入规模 n 的增加,额外空间的需求主要由数组 arr 决定,因此该函数的空间复杂度O(n)

3.3 递归函数的空间复杂度

        递归函数在执行过程中可能会产生递归调用栈,这会占用额外的内存空间。如果递归调用的深度与输入规模 n 成正比,则称该递归函数具有线性空间复杂度 O(n)

void Recursion(int n)
{if (n == 1){printf("递归函数");}else if (n > 1){Recursion(n - 1);  // 注意:函数名应一致}
}int main()
{Recursion(5);  // 注意:函数名应一致
}

        在这个示例中,Recursion 函数是一个递归函数,其逻辑如下:

  1. 基本情况:当 n == 1 时,函数打印 “递归函数” 并返回。
  2. 递归情况:当 n > 1 时,函数递归调用自身,传入 n - 1

        每次递归调用都会在调用栈上留下一个帧(frame),直到达到基本情况为止。因此,递归调用的深度与 n 直接相关。具体来说,当 n = 5 时,递归调用的深度为 5 层,如下图所示。

        每次递归调用都会在栈上保留一些信息,包括局部变量、参数和返回地址等。假设每次递归调用的栈帧大小为固定值 k,那么对于输入规模 n,总的栈空间为 k×n。因此,该递归函数的空间复杂度为 O(n)。

3.4 平方级空间复杂度 O(n^2)

        如果一个算法在执行过程中所需的额外空间与输入规模 n 的平方成正比,则称该算法具有平方级空间复杂度 O(n^2)。这意味着随着输入规模 n 的增加,算法所需的额外空间会以 n^2 的形式增长。

void test4(int n)
{int i, j;int arr[n][n];
}

        在这个示例中,test4 函数的主要逻辑是声明一个大小为 n×n 的二维数组 arr。我们来分析一下该函数的空间复杂度

  1. 函数参数 n:作为函数的输入参数,n 占用 4 个字节。
  2. 局部变量 ij:在函数内部声明的局部变量 i 和 j 各自占用 4 个字节。
  3. 二维数组 arr:数组 arr 的大小为 n×n,每个 int 类型的元素占用 4 个字节,因此数组 arr 占用 4*n^2 个字节。

        综上所述,整个函数所需的额外空间为:4 (for n) + 4 (for i) + 4 (for j) + 4n^2 (for arr) = 4n^2+12 字节,随着输入规模 n 的增加,额外空间的需求主要由二维数组 arr 决定,因此该函数的空间复杂度为 O(n^2)。


空间复杂度时间复杂度的关系

        在算法分析中,时间和空间是两个重要的性能指标。时间复杂度衡量的是算法执行所需的时间,而空间复杂度衡量的是算法执行所需的空间。尽管在许多情况下,我们更关注时间复杂度,因为算法的执行速度直接影响用户体验和系统的响应时间,但空间复杂度同样不容忽视,尤其是在资源受限的环境中。

4.1 时间复杂度 vs 空间复杂度

        时间复杂度衡量算法执行所需的时间,通常用大 O 渐进表示法表示,如 O(1)、O(n)、O(n^2) 等。时间复杂度直接影响算法的效率和性能。

        空间复杂度衡量算法执行所需的额外存储空间,同样用大 O 渐进表示法表示,如 O(1)、O(n)、O(n^2) 等。空间复杂度直接影响算法对内存资源的使用。

4.2 以空间换时间

        在许多情况下,我们可以通过增加额外的空间来减少算法的执行时间。这种策略称为 “以空间换时间”。例如:

  • 缓存:通过缓存频繁访问的数据,可以减少重复计算,提高算法的执行速度。
  • 预处理:通过预先计算和存储中间结果,可以在后续的查询中快速获取结果,从而减少计算时间。
  • 数据结构优化:使用更高效的数据结构(如哈希表、树等)可以减少查找和插入操作的时间复杂度

4.3 空间复杂度的重要性

        尽管时间复杂度通常更为重要,但空间复杂度在以下情况下同样关键:

  • 资源受限的环境:在嵌入式系统、移动设备等资源受限的环境中,内存资源非常宝贵,高空间复杂度的算法可能导致内存溢出或系统崩溃。
  • 大规模数据处理:在处理大规模数据时,高空间复杂度的算法可能会消耗大量内存,导致系统性能下降或无法运行。
  • 分布式系统:在分布式系统中,内存使用会影响数据传输和节点间的通信效率,高空间复杂度的算法可能会增加网络带宽的使用。

4.4 平衡时间和空间

        在实际应用中,通常需要在时间和空间之间找到一个平衡点。以下是一些优化策略:

  • 空间优化:通过使用更高效的数据结构和算法,减少内存使用。
  • 时间优化:通过缓存、预处理等技术,减少计算时间。
  • 混合策略:结合时间和空间优化,根据具体需求选择最合适的方案。

http://www.ppmy.cn/embedded/129146.html

相关文章

010_django基于spark的电力能耗数据分析系统的设计与实现2024_s120960s

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

HALCON数据结构之矩阵

1.1矩阵的创建、设置和访问 *1、矩阵的创建*创建单位矩阵 create_matrix (3, 3, identity, MatrixID1)*创建一个全是常数的矩阵 create_matrix (3, 3, 7, MatrixID2)*为主对角线上的所有元素都被设置为参数Value的值 create_matrix (3, 3, [3,7,1], MatrixID3)*为矩阵的所有元…

模型的部署:服务端与客户端建立连接(Flask)

目录 一、服务端部署&#xff08;使用Flask&#xff09; 1.安装Flask 2.加载模型&#xff08;这里以识别图片的类型模型为例&#xff09; 3.定义API端点 4.运行Flask应用 二、客户端请求 1.安装HTTP客户端库 2.发送请求 请求成功示例&#xff1a; 监控与日志 总结 在…

C语言实践中的补充知识 Ⅱ

一、在C语言中&#xff0c;% 7.2f 是一个格式说明符&#xff0c;通常用于printf或sprintf等函数中&#xff0c;用于控制浮点数的输出格式。 这里的 % 是格式说明符的开始符号。 7 表示字段宽度。这意味着输出的浮点数将至少占用7个字符的宽度。如果浮点数的实际宽度小于7个字符…

【Flutter】页面布局:流式布局(Wrap、Flow)

在移动应用开发中&#xff0c;布局是非常重要的一部分&#xff0c;尤其是当我们需要处理动态或自适应的内容时。Flutter 提供了几种布局方式来帮助开发者处理复杂的 UI 场景&#xff0c;其中 Wrap 和 Flow 是常用的流式布局组件。它们在处理多个子组件时表现优越&#xff0c;尤…

智发展 智飞跃 亚信安全与新华三深化战略合作

10月16日&#xff0c;亚信安全与新华三集团共同宣布&#xff0c;双方正式签署战略合作协议&#xff0c;双方将基于各自在硬件及软件安全领域的能力和优势&#xff0c;在产品、解决方案、市场拓展等多个领域深入合作&#xff0c;赋能千行百业数字化转型与变革。 亚信安全CEO马红…

【Git】Gitlab进行merge request的时候,出现待合并分支合并了主分支的问题的解决

最近在公司开始用merge request进行代码合并了。 然后不知道为啥&#xff0c;如果待合并分支&#xff08;A&#xff09;进行merge request到主分支&#xff08;B&#xff09;的时候&#xff0c;如果A和B有冲突&#xff0c;然后我在gitlab上使用页面进行冲突的解决&#xff0c;比…

vector的模拟实现

1.迭代器失效 在上一篇中因为插入导致的扩容&#xff0c;扩容则pos指向的是之前的空间&#xff0c;导致了野指针的出现&#xff0c;没有扩容&#xff0c;使pos的位置意义改变&#xff0c;由于数据挪动&#xff0c;pos不再指向原来的位置&#xff0c;认为上面俩种迭代器失效。(…