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

目录

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),因为除了输入参数外,没有使用额外的数据结构或变量来存储数据。无论输入规模如何变化,空间占用都是固定的。


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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/77000.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【附安装包】2023最新版Python安装详细教程!一键安装,永久使用

一、python官网 Python官网主要有python的About (简介)、Downloads (下载)、Documentation(文档)、Community (团体)、Success Stories (成功案例)、News (新闻)、Events (事件动态)等栏目。 Python官网地址&#xff1a;https://www.python.org/ 【领取方式见文末】 二、在…

免费的低代码助力售后工单管理:快速搭建,高效定制

编者按&#xff1a;本文旨在阐述免费且高效的低代码平台在实现售后工单管理系统方面的优势、功能及其作用。这些优势和功能对于提高企业的服务质量和效率具有重要的意义。 关键词&#xff1a;低代码平台、售后工单系统、私有化部署 1.售后工单系统有什么作用&#xff1f; 售后工…

day55:C++ day5,运算符重载剩余部分、静态成员、继承

#include <iostream> #include <cstring> #define pi 3.14 using namespace std;class Shape { protected:double round;double area; public://无参构造Shape():round(40),area(100){cout<<"Shape::无参构造函数&#xff0c;默认周长为40&#xff0c;面…

Python的命令行参数

Python的命令行参数&#xff0c;提供了很多有用的功能&#xff0c;可以方便调试和运行&#xff0c;通过man python就能查看&#xff0c;以下是一些常用参数使用实例和场景: 1. -B参数 在import时候&#xff0c;不产生pyc或者pyo文件: 比如有程序main.py如下: from Hello im…

软件测试/测试开发丨使用ChatGPT自动进行需求分析

简介 在实际工作过程中&#xff0c;常常需要拿到产品的PRD文档或者原型图进行需求分析&#xff0c;为产品的功能设计和优化提供建议。 而使用ChatGPT可以很好地帮助分析和整理用户需求。 实践演练 接下来&#xff0c;需要使用ChatGPT 辅助我们完成需求分析的任务 注意&…

数据链路层相关知识

数据链路层的作用 两个设备(同一种数据链路节点)之间进行传递数据 认识以太网 "以太网"是一种技术标准;既包含了数据链路层的内容,也包含了一些物理层的内容.例如:规定了网络拓扑结构,访问控制方式,传输速率等;以太网中的网线必须使用双绞线;传输速率有10M,100M,1…

Linux设备驱动——自动创建设备节点udev机制的实现过程

创建设备文件的机制有以下下列几种&#xff1a; mknod命令&#xff1a;手动创建设备节点的命令devfs:可以用于创建设备节点&#xff0c;创建设备节点的逻辑在内核空间&#xff08;内核2.4版本之前使用&#xff09;udev:自动创建设备节点的机制&#xff0c;创建设备节点的逻辑在…

python机器人编程——用python实现一个写字机器人

目录 一、前言二、整体框架2.1 系统构成2.2 硬件介绍2.2.1主要组成部分2.2.2机械结构2.2.3驱动及控制主板PS电机驱动原理简介: 2.2.4其余部分 2.3 机器人python程序框架2.3.1通信服务模块2.3.2消息处理模块2.3.3轨迹解析模块2.3.4机械臂逆解模块2.3.5写字板模块 三、机械臂的建…

0门槛限制!快来领取你的专属元宇宙虚拟展厅!

数字化时代中&#xff0c;元宇宙虚拟展厅仿佛成为了一种新的潮流&#xff0c;虚拟展厅的出现为我们呈现出了一个超越现实的全新世界。元宇宙虚拟展厅以其多样性、互动性、沉浸式展示为特点&#xff0c;同产品进行交互&#xff0c;创造出逼真的虚拟环境&#xff0c;为广大用户打…

八股——const 关键字

1.const作用 作用&#xff1a;const用于保护指针指向数据不被修改 测试代码1 显示数组的函数不小心修改了指针指向的值&#xff0c;这时候没有加const关键字&#xff0c;编译器不会报错 #include <stdio.h> void showar(int ar[]);int main(void) {int ar[4]{2,3,4,5…

Springboot后端跨域处理

跨域 当一台服务器资源从另一台服务器&#xff08;不同的域名或者端口&#xff09;请求一个资源或者接口&#xff0c;就会发起一个跨域HTTP请求。 同源&#xff1a;协议、域名、端口都相同 只要一个不同&#xff0c;就是跨域。 例子 请求方响应方是否跨域原因http://www.ba…

element-ui switch开关组件二次封装,添加loading效果,点击时调用接口后改变状态

先看效果&#xff1a; element-ui中的switch开关无loading属性&#xff08;在element-plus时加入了&#xff09;&#xff0c;而且点击时开关状态就会切换&#xff0c;这使得在需要调用接口后再改变开关状态变得比较麻烦。 思路&#xff1a;switch开关外包一层div&#xff0c;给…

LeetCode518. 零钱兑换 II 以及 动态规划相关的排列组合问题

文章目录 一、题目二、题解方法一&#xff1a;完全背包问题的变体&#xff08;版本1&#xff09;方法二&#xff1a;完全背包问题变体&#xff08;版本2&#xff09; 三、拓展&#xff1a;先遍历物品后遍历背包vs先遍历背包后遍历物品先遍历物品后遍历背包&#xff08;组合问题…

NAT实验:构建复杂网络拓扑,实现互联网访问与FTP发布

文章目录 一、实验背景与目的二、实验拓扑三、实验需求四、实验解法1.配置链路上各个接口的IP地址。2.在私网中配置单臂路由3.在R1和R3上配置默认路由指向公网。4.私网A通过NAPT使vlan 20和vlan 10能够使用R1的公网访问互联网。5.私网B通过在R3上配置EASY IP访问互联网。6.私网…

Docker部署EMQX

1、简介 EMQ X (Erlang/Enterprise/Elastic MQTT Broker) 是基于 Erlang/OTP 平台开发的开源物联网 MQTT 消息服务器。 Erlang/OTP是出色的软实时 (Soft-Realtime)、低延时 (Low-Latency)、分布式 (Distributed)的语言平台。 MQTT 是轻量的 (Lightweight)、发布订阅模式 (Pu…

labelme2voc 标签重叠/覆盖问题

使用labelme自带的 labelme2voc.py转换voc数据集时可能标签重叠

Maven Helper mvn项目冲突如何解决

一般用这款插件来查看maven的依赖树。 一、安装&#xff1a; File-->setting--->Plugins--->在搜索框中填写Maven Helper然后搜索&#xff0c;单击Install按钮进行安装&#xff0c;装完重启IDE。 二、使用 当Maven Helper 插件安装成功后&#xff0c;打开项目中的p…

AMD R7 7840HS 核显 780M 性能怎么样

目录 1. 基本数据 2.性能对比 2.1对比一 2.2 对比二 3.综合 1. 基本数据 2.性能对比 2.1对比一 锐龙77840h相当于i几 答&#xff1a;类似于I7-12700H R7 7840H介于13500-13700之间。 R7 7840H是AMD锐龙旗下高性能的一款处理器&#xff0c;主要应用在主流的游戏本中。各…

GO语言篇之发布开源软件包

GO语言篇之发布开源软件包 文章目录 GO语言篇之发布开源软件包新建仓库拉取到本地初始化项目编写代码提交代码发布引用软件包 我们写GO语言程序的时候难免会引用第三方的软件包&#xff0c;那么你知道别人是怎么发布自己的软件包吗&#xff0c;别急&#xff0c;这篇博客教你怎么…

数据分享|R语言分析上海空气质量指数数据:kmean聚类、层次聚类、时间序列分析:arima模型、指数平滑法...

全文链接&#xff1a;http://tecdat.cn/?p30131 最近我们被客户要求撰写关于上海空气质量指数的研究报告。本文向大家介绍R语言对上海PM2.5等空气质量数据&#xff08;查看文末了解数据免费获取方式&#xff09;间的相关分析和预测分析&#xff0c;主要内容包括其使用实例&…