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

【算法基础】时间复杂度和空间复杂度

目录

1 算法的评价

2 算法复杂度

2.1 时间复杂度(Time Complexity)

2.1.1 如何计算时间复杂度:

2.1.2 常见的时间复杂度类别与示例

2.2 空间复杂度

2.2.1 如何计算空间复杂度

2.2.2 常见的空间复杂度与示例

3 时间复杂度和空间复杂度计算示例

例子1:计算数组中所有元素的和。

例子2:快速排序算法。

例子3:递归实现斐波那契数列。

例子4:非递归实现的斐波那契数列。

例子5:二分查找算法。

例子6:冒泡排序算法。


1 算法的评价

        评价算法的性能和效果是计算机科学和数据科学中的关键任务之一。如何评价算法的优劣可以从以下几方面展开:

        时间复杂度和空间复杂度是算法性能分析的关键指标,它们用于衡量算法在处理不同规模输入时的时间和空间资源消耗。

2 算法复杂度

2.1 时间复杂度(Time Complexity)

        时间复杂度是指在算法执行过程中,所需的时间资源与问题规模之间的关系。它主要衡量的是算法的执行效率,用于评估算法在不同规模数据下的操作时间。

        时间复杂度通常使用大O符号表示,表示算法运行时间的增长率。

         需要注意的是,时间复杂度只考虑算法的主要操作数量级,忽略了常数因子和低阶项。因此,两个时间复杂度相同的算法在实际执行中可能有着不同的执行效率。

2.1.1 如何计算时间复杂度:

  • 分析每个操作的时间复杂度,包括循环、条件语句和函数调用。
  • 计算每个操作的执行次数,通常是输入规模的函数。
  • 合并所有操作的复杂度,通常选择最大的那个作为算法的时间复杂度。 

时间复杂度的计算涉及以下几个方面:

  1. 基本操作次数: 时间复杂度的计算通常关注算法中执行的基本操作次数,例如赋值操作、比较操作、算术运算等。通常将这些操作的数量与输入规模相关联。

  2. 循环结构: 如果算法包含循环结构(例如for循环、while循环),需要考虑循环的迭代次数以及每次迭代中的基本操作数量。

  3. 递归调用: 对于递归算法,需要考虑递归的深度以及每次递归调用的时间复杂度。通常使用递归方程(递归关系式)来表示递归算法的时间复杂度。

  4. 分支结构: 如果算法包含分支结构(例如if语句),需要考虑每个分支的执行次数以及分支中的基本操作数量。

  5. 输入规模: 时间复杂度的计算通常与输入规模有关。输入规模表示算法操作的数据量或问题的大小,通常用符号n表示。

2.1.2 常见的时间复杂度类别与示例

  1. 常数时间复杂度(O(1)):无论问题规模多大,算法的执行时间都保持不变。例如,直接访问数组中的一个元素。

  2. 线性时间复杂度(O(n)):随着问题规模的增大,算法的执行时间也按线性比例增长。例如,遍历一个数组或链表中的所有元素。

  3. 对数时间复杂度(O(logn)):算法执行时间随着问题规模的增大而增长,但不是线性关系,而是以对数速率增长。例如,二分查找算法。

  4. 平方时间复杂度(O(n^2)):算法的执行时间与问题规模的平方成正比。例如,双重循环嵌套的算法。

  5. 指数时间复杂度(O(2^n)):算法的执行时间呈指数级增长,非常低效。例如,穷举法解决NP完全问题。    

O(1) - 常数时间复杂度: 算法的执行时间是固定的,与输入规模无关。示例:

def constant_time_algorithm(arr):return arr[0]

O(log n) - 对数时间复杂度: 算法的执行时间随着输入规模的增加以对数方式增加。示例:

def binary_search(arr, target):low, high = 0, len(arr) - 1while low <= high:mid = (low + high) // 2if arr[mid] == target:return midelif arr[mid] < target:low = mid + 1else:high = mid - 1return -1

O(n) - 线性时间复杂度: 算法的执行时间与输入规模成正比。示例

def linear_search(arr, target):for i in range(len(arr)):if arr[i] == target:return ireturn -1

O(n^2) - 平方时间复杂度: 算法的执行时间与输入规模的平方成正比。示例:

def bubble_sort(arr):n = len(arr)for i in range(n):for j in range(0, n-i-1):if arr[j] > arr[j+1]:arr[j], arr[j+1] = arr[j+1], arr[j]

2.2 空间复杂度(Space Complexity)

        空间复杂度是指算法在执行过程中所需的额外内存空间,它与问题规模之间的关系。空间复杂度用于评估算法的内存占用情况和资源消耗。

        通常使用大O符号表示空间复杂度,表示算法所需的额外内存空间与问题规模之间的增长关系。

2.2.1 如何计算空间复杂度

  • 分析算法的每个数据结构、变量和递归调用,以确定它们的空间占用。
  • 计算每个数据结构和变量的空间占用,通常是常数项和与输入规模相关的项的和。
  • 合并所有空间占用,通常选择最大的那个作为算法的空间复杂度。

空间复杂度的计算包括以下几个方面:

  1. 固定内存消耗: 指算法在运行过程中需要固定数量的内存空间,与输入规模无关。常见的固定内存消耗包括函数参数、常量变量、全局变量等。

  2. 额外数据结构: 如果算法使用了额外的数据结构来存储信息,如数组、列表、树、堆栈、队列等,需要考虑这些数据结构所占用的内存空间。通常需要考虑数据结构的大小和数量。

  3. 递归调用: 递归算法会使用栈空间来存储每一次递归调用的状态。递归的深度和每次递归调用的内存消耗会影响空间复杂度。

  4. 临时变量: 算法中使用的临时变量和计算过程中的中间结果也会占用内存空间。需要考虑这些变量的数量和大小。

  5. 输入数据的存储: 输入数据的存储也需要考虑在内。如果算法需要将整个输入数据存储在内存中,则空间复杂度与输入数据的大小成正比。

2.2.2 常见的空间复杂度与示例

  1. 常数空间复杂度(O(1)):算法所需的额外内存空间是一个常量值,不随问题规模的增大而改变。例如,只使用固定数量的变量或常量大小的数组。

  2. 线性空间复杂度(O(n)):算法所需的额外内存空间随问题规模的增大而线性增长。例如,需要根据输入构建一个同等大小的新数据结构。

  3. 平方空间复杂度(O(n^2)):算法所需的额外内存空间随问题规模的增大而平方级增长。例如,需要构建一个二维数组来存储所有可能的组合。

  4. 指数空间复杂度(O(2^n)):算法所需的额外内存空间随问题规模的增大而以指数级增长。例如,需要存储所有可能的子集或排列。

        需要注意的是,空间复杂度只考虑算法本身所需的额外内存空间,不包括输入数据所占用的存储空间。另外,空间复杂度也可以根据最坏情况或平均情况来进行分析。

O(1) - 常数空间复杂度: 算法的内存使用与输入规模无关,占用固定的内存空间。示例:

def constant_space_algorithm(arr):result = 0for num in arr:result += numreturn result

O(n) - 线性空间复杂度: 算法的内存使用与输入规模成正比。示例:

def linear_space_algorithm(n):arr = [0] * nreturn arr

O(n^2) - 平方空间复杂度: 算法的内存使用与输入规模的平方成正比。示例:

def quadratic_space_algorithm(n):arr = [[0] * n for _ in range(n)]return arr

3 时间复杂度和空间复杂度计算示例

例子1:计算数组中所有元素的和。

def sum_array(arr):sum = 0for num in arr:sum += numreturn sum

时间复杂度:O(n),其中n是数组中的元素数量。遍历数组需要依次访问每个元素一次,因此时间复杂度与数组的大小成线性关系。

空间复杂度:O(1)。算法只使用了一个额外的变量存储累加和,并没有占用随问题规模变化的额外内存。


例子2:快速排序算法。

def quicksort(arr, left, right):if left < right:pivot = partition(arr, left, right)quicksort(arr, left, pivot - 1)quicksort(arr, pivot + 1, right)def partition(arr, left, right):pivot = arr[right]i = left - 1for j in range(left, right):if arr[j] <= pivot:i += 1arr[i], arr[j] = arr[j], arr[i]arr[i + 1], arr[right] = arr[right], arr[i + 1]return i + 1

时间复杂度:最好情况下为O(nlogn),最坏情况下为O(n^2)。快速排序平均情况下的划分操作需要O(n)的时间复杂度,且需要递归n次,因此总体复杂度为O(nlogn)。但在最坏情况下,划分不平衡导致某一边的规模接近n,此时的时间复杂度变为O(n^2)。

空间复杂度:最好情况下为O(logn),最坏情况下为O(n)。快速排序使用递归调用,每次递归调用都需要保存当前函数的堆栈信息,而在最坏情况下,可能需要递归n次,所以空间复杂度为O(n)。而在最好情况下,递归调用树的高度为logn,因此空间复杂度为O(logn)。


例子3:递归实现斐波那契数列。

def fibonacci(n):if n <= 0:return 0if n == 1:return 1return fibonacci(n - 1) + fibonacci(n - 2)
时间复杂度:指数级别,为O(2^n)。由于递归调用会重复计算相同的斐波那契数,时间复杂度呈指数级增长。
空间复杂度:最好和最坏情况下均为O(n),取决于递归调用的最大深度n。每次递归调用都需要在堆栈中保存函数的局部变量和参数,因此空间复杂度为O(n)。 

该代码实现了递归方式计算斐波那契数列的函数。

时间复杂度:指数级别,为 O(2^n)。每次递归调用都会产生两个新的递归调用,因此递归树的总节点数是指数级别的,递归树的深度是 n。所以,总体的时间复杂度是 O(2^n)。

空间复杂度:指数级别,为 O(n)。在递归调用过程中,需要使用栈来保存每次递归调用的参数和局部变量。由于递归树的深度是 n,所以空间复杂度是 O(n)。

需要注意的是,由于斐波那契数列的计算可以通过动态规划或迭代的方式进行优化,以降低时间复杂度和空间复杂度。递归方式计算斐波那契数列在面对较大的 n 值时,会导致非常高的时间和空间消耗。

 例子4:非递归实现的斐波那契数列。

def fibonacci(n):if n <= 0:return 0a = 0b = 1for _ in range(2, n+1):c = a + ba = bb = creturn b

这段代码实现了求解斐波那契数列的函数。

该代码的时间复杂度是 O(n),其中 n 是要计算的斐波那契数的索引。在 for 循环中,需要执行 n-1 次加法操作。因此,时间复杂度是线性级别的。

该代码的空间复杂度是 O(1),因为除了输入参数外,只使用了常数空间来存储变量 a、b 和 c。无论输入的 n 多大,空间占用都是固定的。

例子5:二分查找算法。

def binary_search(arr, target):low = 0                    # 常数时间复杂度high = len(arr) - 1        # 常数时间复杂度while low <= high:mid = (low + high) // 2    # 常数时间复杂度if arr[mid] == target:return midelif arr[mid] < target:low = mid + 1else:high = mid - 1return -1

该算法的时间复杂度为O(logn),在二分查找算法中,每次迭代会将问题规模缩小一半,因此时间复杂度为对数级别。具体而言,时间复杂度是由二分查找的迭代次数决定的。

空间复杂度是 O(1),因为除了输入参数外,没有使用额外的数据结构或变量来存储数据。无论输入规模如何变化,空间占用都是固定的。

例子6:冒泡排序算法。

def bubble_sort(arr):n = len(arr)for i in range(n):              # 线性时间复杂度for j in range(0, n-i-1):   # 线性时间复杂度if arr[j] > arr[j+1]:arr[j], arr[j+1] = arr[j+1], arr[j]

时间复杂度是 O(n^2),其中 n 是数组 arr 的长度。冒泡排序算法的时间复杂度由两层嵌套循环决定。外层循环执行 n 次,内层循环从 0 到 n-i-1 遍历,其中 i 是外层循环的迭代次数。因此,总的比较次数是 n + (n-1) + (n-2) + ... + 2 + 1,即等差数列求和公式,可以简化为 (n^2 - n) / 2,近似为 n^2。因此,该代码的时间复杂度是 O(n^2)。

该代码的空间复杂度是 O(1),因为除了输入参数外,没有使用额外的数据结构或变量来存储数据。无论输入规模如何变化,空间占用都是固定的。


        计算时间复杂度和空间复杂度通常需要分析算法的每个操作以及它们的频率和内存占用。最终,选择合适的数据结构和算法以及考虑性能优化策略都有助于确保算法在不同规模的问题上都能高效运行。

 

相关文章:

【算法基础】时间复杂度和空间复杂度

目录 1 算法的评价 2 算法复杂度 2.1 时间复杂度&#xff08;Time Complexity&#xff09; 2.1.1 如何计算时间复杂度&#xff1a; 2.1.2 常见的时间复杂度类别与示例 2.2 空间复杂度 2.2.1 如何计算空间复杂度 2.2.2 常见的空间复杂度与示例 3 时间复杂度和空间复杂度…...

解决微信小程序不支持TextEncoder/TextDecoder对象

问题描述&#xff1a;在使用小程序开发者工具开发小程序中使用到了CRC算法&#xff0c;其中有一行代码使用到了TextEncoder对象&#xff0c;在开发工具中一切正常&#xff0c;到手机上会报出错误错误如下&#xff1a; MiniProgramError TextEncoder is not defined ReferenceEr…...

Qt下SVG格式图片应用

SVG格式图片介绍 svg格式图片又称矢量图&#xff0c;该种格式的图片不同于png等格式的图片&#xff0c;采用的并不是位图的形式来组织图片&#xff0c;而是采用线条等组织图片&#xff0c;svg格式是图片的文件格式是xml&#xff0c;可以通过文件编译器打开查看svg格式内容。 …...

python异常处理

参考语法&#xff1a;https://docs.python.org/zh-cn/3/tutorial/errors.html 在编写代码的时候&#xff0c;如果你写的程序出现报错&#xff0c;程序就会停止运行&#xff0c;后面的代码就不再执行。 如果程序发生错误&#xff0c;可以在代码中添加异常处理&#xff0c;保证程…...

go get命令不再具有安装功能

go get功能呢 一直以来&#xff0c;我们知道go get命令可以借助代码管理工具通过远程拉取或更新代码包及其依赖包&#xff0c;并自动完成编译和安装。整个过程就像安装一个App一样简单。 go get命令可以动态获取远程代码包&#xff0c;命令在内部实际上分成了两步操作&#x…...

合宙Air724UG LuatOS-Air lvgl7-lvgl(矢量字体)

如何用开发板实现lvgl加载外部矢量字体功能 目录名称 如何用开发板实现lvgl加载外部矢量字体功能 简介材料准备API 说明步骤 1. 将字库芯片接在模块spi上2. 版本定制3. 初始化spi4. 设置字体5.字体使用测试固件和脚本显示效果字号灰度最佳粗细值对应表常见问题 1. 设置68号字体…...

LRU的实现

题目内容 实现一个 LRUCache 类&#xff0c;三个接口&#xff1a; LRUCache(int capacity) 创建一个大小为 capacity 的缓存get(int key) 从缓存中获取键为 key 的键值对的 valueput(int key, int value) 向缓存中添加键值对 (key, value) 要求 get 和 put 的均摊时间复杂度…...

consul 备份还原导入导出

正文 工作中要保证生产环境部署的consul的集群能够安全稳定地对外提供服务&#xff0c;即使出现系统故障也能快速恢复&#xff0c;这里将讲述部分的备份还原操作及KV的导入导出操作。 备份与还原 配置文件、服务器状态 需要备份的主要有两类数据&#xff1a;consul相关的配置文…...

6.网络编程套接字(下)

文章目录 4.TCP流套接字编程4.1ServerSocket API4.2Socket API4.3TCP中的长短连接4.4示例一&#xff1a;一发一收&#xff08;长连接&#xff09;4.4.1TCP服务端4.4.2TCP客户端 4.5示例二&#xff1a;请求响应&#xff08;短连接&#xff09;4.5.1TCP服务端4.5.2TCP客户端 4.6再…...

4.3-内置后置PostProcess处理器深度讲解

在reader里面注册了很多Bean定义 reader会调取register()来注册配置类 调用上句&#xff0c;就会把配置类注册到BeanDefinitionMap中去 配置类有了、解析配置类的处理器有了 然后&#xff0c; 在第三步refresh() 进行IOC容器刷新中的invokeBeanPostProcessors(beanFactory…...

LeetCode(力扣)45. 跳跃游戏 IIPython

LeetCode45. 跳跃游戏 II 题目链接代码 题目链接 https://leetcode.cn/problems/jump-game-ii/description/ 代码 class Solution:def jump(self, nums: List[int]) -> int:if len(nums) 1:return 0curdis 0nextdis 0step 0for i in range(len(nums)):nextdis max(…...

mysql5.8 免安装版(压缩包)win10 安装

目录 1、下载MySQL5.82、如何安装、配置my.ini配置注意 3初始化mysql3.1. 初始化mysql3.2. 安装mysql服务3.3. 启动mysql3.4. 登录mysql3.5. 修改root密码3.6. 配置远程连接 Mysql5.8安装踩坑记录&#xff0c;推荐使用Docker安装&#xff0c;我是电脑虚拟化可能会蓝屏没用这个功…...

STM32-HAL库06-硬件IIC驱动FM24CL16B非易失存储器

STM32-HAL库06-IIC驱动FM24CL16B非易失存储器 一、所用材料&#xff1a; STM32VGT6自制控制板 STM32CUBEMX&#xff08;HAL库软件&#xff09; MDK5 二、所学内容&#xff1a; 通过HAL库的硬件IIC对FM24CL16B存储器进行写与读取操作。 三、CUBEMX配置&#xff1a; 第一步…...

python-wordcloud词云

导入模块 from wordcloud import WordCloud import jieba import imageio import matplotlib.pyplot as plt from PIL import ImageGrab import numpy as npwordcloud以空格为分隔符号&#xff0c;来将文本分隔成单词 PIL pillow模块 img imageio.imread(image.png)这行代码…...

单元测试与自测

单元测试在百度百科的定义&#xff1a; 自测在百度百科的定义&#xff1a; 单元测试是测一个类或一个函数&#xff0c;自立门第main函数&#xff0c;不依赖于项目&#xff0c;预期的是这个类或函数是没有问题的。程序编码完成之后至各种测试再到用户使用出现的任何bug都是单元测…...

2023-09-12 LeetCode每日一题(课程表 IV)

2023-03-29每日一题 一、题目编号 1462. 课程表 IV二、题目链接 点击跳转到题目位置 三、题目描述 你总共需要上 numCourses 门课&#xff0c;课程编号依次为 0 到 numCourses-1 。你会得到一个数组 prerequisite &#xff0c;其中 prerequisites[i] [ai, bi] 表示如果你…...

RabbitMQ基础

目录 MQ MQ概述 MQ 的优势 1.应用解耦 2.异步提速 3.削峰填谷 MQ 的劣势 1.系统可用性降低 2.系统复杂度提高 3.一致性问题 使用 MQ 需要满足什么条件呢&#xff1f; RabbitMQ 简介 ​编辑RabbitMQ 中的相关概念 RabbitMQ 提供了 6 种工作模式 JMS java实现Ra…...

ITIL 4—创建、交付和支持—创建、交付和支持服务的价值流

4. 创建、交付和支持服务的价值流 本章节提供了有关如何&#xff1a; 记录一个价值流以理解工作流程如何贯穿该组织了解创建一个新服务的原型价值流了解支持一个现场服务的原型价值流 本章将帮助从业者理解&#xff1a; 价值流在 服务价值系统(SVS) 中的作用价值流的分类如…...

微信怎么给自己发消息

前段时间看到一份数据调查&#xff0c;说是到目前为止&#xff0c;全球使用微信的用户已达到10亿多人次&#xff0c;天啊&#xff0c;多么强大的用户群体&#xff01; 这么多人喜欢使用微信&#xff0c;相信大家都知道&#xff0c;微信里面有一个特俗功能&#xff0c;可以自己…...

正交试验设计法

正交实验设计 一、什么是正交试验设计法&#xff1f; 是一种成对测试交互的系统的统计方法。它提供了一种能对所有变量对的组合进行典型覆盖&#xff08;均匀分布&#xff09;的方法。 可以从大量的试验点中挑出适量的、有代表性的点&#xff0c;利用“正交表”&#xff0c;…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...