【数据结构起航】:衡量算法的好坏--时间空间复杂度
时间复杂度和空间复杂度

文章目录
- 时间复杂度和空间复杂度
- 1.算法效率
- 1.1算法复杂度
- 1.2复杂度在OJ里的应用
- 2.时间复杂度
- 2.1时间复杂度的概率
- 2.2大O渐进表示法
- 推导大O阶方法:
- 2.3时间复杂度的举例计算
- 3.空间复杂度
- 3.1空间复杂度的举例计算
- 4.复杂度各量级对比
1.算法效率
1.1算法复杂度
如何衡量一个算法的好坏呢?例如小红和小明面对一个OJ题,设计出各自的算法,解决了这道OJ题,那么我们如何评断是谁的算法更好呢?答案是:衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即我们稍后要谈的时间复杂度和空间复杂度。
时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,也根据摩尔定律,计算机的存储容量已经达到了很高的程度。所以我们如今相比空间复杂度,我们更关注时间复杂度。
1.2复杂度在OJ里的应用

而在校招中,笔试题主要采取OJ的形式,所以复杂度在校招的考察中是很重要的。例如:要实现一个排序功能,你使用的是冒泡排序,而别人用的是快速排序,虽然元素较少时排序效果差别不大,但是在算法效率上后者要优良很多。
2.时间复杂度
2.1时间复杂度的概率
⚠误区:时间复杂度并不是指一个程序运行所需要的时间,因为硬件设备的不同,程序运行的时间也不同。所以一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。
在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。
举例:
void Func1(int N)
{int count = 0;//基本操作执行次数:N^2 for (int i = 0; i < N ; ++ i){for (int j = 0; j < N ; ++ j){++count;}}//基本操作执行次数:2*Nfor (int k = 0; k < 2 * N ; ++ k){++count;}//基本操作执行次数:10int M = 10;while (M--){++count;}printf("%d\n", count);
}
Func1 执行的基本操作次数 :
F(N)=N2+2∗N+10F(N) = N^2 + 2*N + 10 F(N)=N2+2∗N+10
- N = 10 F(N) = 130
- N = 100 F(N) = 10210
- N = 1000 F(N) = 1002010
2.2大O渐进表示法
上述例子中,我们发现当N越大时,F(N)受N^2的影响越大,受到2*N+10的影响越小。
实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。
推导大O阶方法:
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
使用大O的渐进表示法以后,Func1的时间复杂度为:
O(N2)O(N^2) O(N2)
- N = 10 F(N) = 100
- N = 100 F(N) = 10000
- N = 1000 F(N) = 1000000
通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。
另外有些算法的时间复杂度存在最好、平均和最坏情况:
例如:n个元素的数组中查找一个数字,如果我们从首元素开始依次向后查找,那么最好、平均、情况为:
最好:第一个元素就是查找的数字,时间复杂度为O(1)
平均:查找的数字是数组中的中位数,时间复杂度为O(N)
最坏:该数组中没有该查找数字,时间复杂度为O(N)
在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)
2.3时间复杂度的举例计算
实例1:
// 计算Func2的时间复杂度? void Func2(int N) {int count = 0;for (int k = 0; k < 2 * N ; ++ k){++count;}int M = 10;while (M--){++count;}printf("%d\n", count); }
基本操作执行了:2N+10次
通过推导大O阶方法知道,时间复杂度为 O(N)
实例2:
// 计算Func3的时间复杂度? void Func3(int N, int M) {int count = 0;for (int k = 0; k < M; ++ k){++count;}for (int k = 0; k < N ; ++ k){++count;}printf("%d\n", count); }
基本操作执行了:M+N次
有两个未知数M和N,时间复杂度为 O(N+M)
若:M>>N,则为O(M);若M<<N,则为O(N);若M和N是一个数量级则为O(M+N)
实例3:
// 计算Func4的时间复杂度? void Func4(int N) {int count = 0;for (int k = 0; k < 100; ++ k){++count;}printf("%d\n", count); }
基本操作执行了:10次
通过推导大O阶方法,时间复杂度为 O(1)
实例4:
// 计算strchr的时间复杂度? const char * strchr ( const char * str, int character );//返回在字符串str中,第一个字符character处的指针

基本操作执行:最好1次,最坏N次
时间复杂度一般看最坏,时间复杂度为 O(N)
实例5:
// 计算BubbleSort的时间复杂度? 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次(有序),最坏执行了(N*(N+1)/2次(逆序)
通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N^2)
实例6:
// 计算BinarySearch的时间复杂度? int BinarySearch(int* a, int n, int x) {assert(a);int begin = 0;int end = n-1; // [begin, end]:begin和end是左闭右闭区间,因此有=号while (begin <= end){int mid = begin + ((end-begin)>>1);if (a[mid] < x)begin = mid+1;else if (a[mid] > x)end = mid-1;elsereturn mid;}return -1; }
基本操作执行最好1次,最坏logN次
时间复杂度为 O(logN)
ps:logN在算法分析中表示是底数为2,对数为N。 有些地方会写成lgN。
实例7:
// 计算阶乘递归Fac的时间复杂度? long long Fac(size_t N) { if(0 == N) return 1;return Fac(N-1)*N; }
通过计算分析发现基本操作递归了N次
时间复杂度为O(N)。
实例8:
// 计算斐波那契递归Fib的时间复杂度? long long Fib(size_t N) { if(N < 3) return 1;return Fib(N-1) + Fib(N-2); }
分析:

通过计算分析发现基本操作递归了2N次
时间复杂度为O(2^N)。
3.空间复杂度
⚠注意:空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义
空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时(额外)占用存储空间大小的量度 。
空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。
注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。
3.1空间复杂度的举例计算
实例1:
// 计算BubbleSort的空间复杂度 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;} }
使用了常数个额外空间
所以空间复杂度为 O(1)
实例2:
// 计算Fibonacci的空间复杂度? // 返回斐波那契数列的前n项 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; }
动态开辟了N个空间
空间复杂度为 O(N)
实例3:
// 计算阶乘递归Fac的空间复杂度? long long Fac(size_t N) {if(N == 0)return 1;return Fac(N-1)*N; }
递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间
就拿二叉树的中序遍历为例,先遍历左子树,再返回,遍历根,再遍历右子树。即遍历完左子树之后,栈帧销毁,已经还给操作系统,空间是在重复利用的。
空间复杂度为O(N)
即:时间是一去不复返的,而空间是可以使用之后返还,再进行重复利用的。
4.复杂度各量级对比


相关文章:
【数据结构起航】:衡量算法的好坏--时间空间复杂度
时间复杂度和空间复杂度 文章目录时间复杂度和空间复杂度1.算法效率1.1算法复杂度1.2复杂度在OJ里的应用2.时间复杂度2.1时间复杂度的概率2.2大O渐进表示法推导大O阶方法:2.3时间复杂度的举例计算3.空间复杂度3.1空间复杂度的举例计算4.复杂度各量级对比1.算法效率 …...
动手学深度学习【1】——线性回归
动手学深度学习网址:动手学深度学习 注:本部分只对基础知识进行简单的介绍并附上完整的代码实现,更多内容可参考上述网址。 简述 需要的准备知识 数学的偏导线性代数 线性模型 回归是能为一个或多个自变量与因变量之间关系建模的一类方…...
Html 相关知识
Html 相关知识 DOM 文档对象模型 (DOM) 是 HTML 和 XML 文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包…...
【冲刺蓝桥杯的最后30天】day1
大家好😃,我是想要慢慢变得优秀的向阳🌞同学👨💻,断更了整整一年,又开始恢复CSDN更新,从今天开始逐渐恢复更新状态,正在备战蓝桥杯的小伙伴可以支持一下哦!…...
c++泛型编程与模板-01函数模板
函数模板的定义 所谓函数模板,实际就是写一个通用函数,返回值和参数的类型都是可变的,用一个特定格式的变量来指定,然后调用此函数的时候,编译器会根据参数的数据类型自动推导出类型,从而达到函数再不同的…...
Golang http请求忘记调用resp.Body.Close()而导致的协程泄漏问题(含面试常见协程泄漏相关测试题)
参考: 知乎:别因为忘记close你的httpclient,造成goroutine泄漏 CSDN:resp.Body.Close() 引发的内存泄漏goroutine个数 先来看几道题,想一想最终的输出结果是多少呢? package mainimport ("fmt"…...
进程信号生命周期详解
信号和信号量半毛钱关系都没有! 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2 查看信号的机制,如默认处理动作man 7 signal SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理…...
2023-03-03干活小计
今天见识了 归一化的重要性:归一化 不容易爆炸 深度了解了学习率:其实很多操作 最后的结果都是改变了lr 以房价预测为例:一个点一个点更新 比较 矩阵的更新: 为什么小批量梯度下降 优于随机梯度下降 优于批量梯度下降ÿ…...
操作系统结构
随着操作系统的不断增多和代码规模的不断扩大,提供合理的结构对提升操作系统的安全与可靠性来说变得尤为重要。 1.分层法 指将操作系统分为若干层,最低层位硬件,最高层为用户接口,每层只能调用紧邻它的低层的功能和服务(类似于计…...
[SSD科普] 固态硬盘物理接口SATA、M.2、PCIe常见疑问,如何选择?
前言犹记得当年Windows 7系统体验指数中,那5.9分磁盘分数,在其余四项的7.9分面前,似乎已经告诉我们机械硬盘注定被时代淘汰。势如破竹的SSD固态硬盘,彻底打破了温彻斯特结构的机械硬盘多年来在电脑硬件领域的统治。SSD数倍于HDD机…...
【Java学习笔记】3.Java 基础语法
Java 基础语法 一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。 对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象ÿ…...
Python基础学习6——if语句
基本概念 if语句为条件判断语句,用来判断if后面的语句是真是假。if的用途有很多,比如作为条件测试可以判断两数是否相等与不等、进行数值笔记等等。例子如下: Lego_price (599, 799, 898) if Lego_price[0] 599:print("Correct!&quo…...
有免费的PDF转Word吗?值得收藏的7个免费 PDF转Word工具请收好
PDF 和 DOC 是人们在工作中广泛使用的两种最流行的文档格式。PDF 是 Adobe 的便携式文档格式,DOC 是 Microsoft 的 Word 文档格式。PDF 是一种更安全可靠的文件格式,因为它很难编辑 PDF 文件,但是有一些称为 PDF 编辑器的工具可用于编辑 PDF …...
Thinkphp6使用RabbitMQ消息队列
Thinkphp6连接使用RabbitMQ(不止tp6,其他框架对应改下也一样),如何使用Docker部署RabbitMQ,在上一篇已经讲了->传送门<-。 部署环境 开始前先进入RabbitMQ的web管理界面,选择Queues菜单,点…...
小成本互联网创业怎么做?低成本创业的方法分享
多数人都会有想法创业,尤其是在互联网上面创业,很多人看到了商机,但是因为成本的原因又放弃了,实际上,小成本也可以互联网创业!那么,小成本互联网创业怎么做?低成本创业的方法在这里…...
六、栈、栈的相关问题
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、栈 1.栈概述 2.栈的实现 2.1 栈的API 2.2 栈的实现 二、栈的括号匹配问题 1.问题描述 2.代码实现 三、逆波兰表达式求值问题 1.问题描述 2.代码 总结 前言 提…...
Java安全停止线程
Thread 类虽提供了一个 stop() 方法(已经被废弃),但由于 stop() 方法强制终止一个正在执行的线程,可能会造成数据不一致的问题,所以在生产环境中最好不要使用。 场景: 由于一些操作需要轮询处理ÿ…...
12 readdir 函数
前言 在之前 ls 命令 中我们可以看到, ls 命令的执行也是依赖于 opendir, readdir, stat, lstat 等相关操作系统提供的相关系统调用来处理业务 因此 我们这里来进一步看一下 更细节的这些 系统调用 我们这里关注的是 readdir 这个函数, 入口系统调用是 getdents 如下调试…...
Windows环境搭建Android开发环境-Android Studio/Git/JDK
Windows环境搭建Android开发环境-Android Studio/Git/JDK 因为休假回来后公司的开发环境由Ubuntu变为了Windows,所以需要重新配置一下开发环境。 工作多年第一次使用Windows环境进行开发工作,作次记录下来。 一、 Git安装 1.1git 标题软件下载 网址&…...
全国爱耳日丨听力受损严重有哪些解决办法
——【科学爱耳护耳,实现主动健康】随着数码电子设备使用越来越方便、日常使用时间越来越长,听力障碍、患上耳道疾病一系列问题也接踵而至,在当下我们必须重视听力健康,采取更科学的听音方式,保护听力健康,…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
