深入理解数据结构(1):复杂度详解

标头风景图片


  • 文章主题:复杂度详解🌱
  • 所属专栏:深入理解数据结构📘
  • 作者简介:更新有关深入理解数据结构知识的博主一枚,记录分享自己对数据结构的深入解读。😄
  • 个人主页:[₽]的个人主页🔥🔥

复杂度详解

  • 前言
  • 算法效率
    • 如何分析一个算法的好坏
    • 算法的复杂度
    • 复杂度在校招中的考察
  • 时间复杂度
    • 时间复杂度的概念
    • 大O的渐进表示法
    • 常见时间复杂度计算举例
  • 空间复杂度
  • 常见复杂度对比
  • 结语

前言

复杂度是计算机领域表示所需资源达到某个大小量级的量度,源自计算复杂性理论1
主要分为时间复杂度与空间复杂度等,通常复杂度会被用来综合分析一个算法的好坏,以下是我对复杂度的详细解释。


算法效率

如何分析一个算法的好坏

如何衡量一个算法的好坏呢?比如对于以下斐波那契数列:

long long Fib(int N)
{if(N < 3)return 1;return Fib(N-1) + Fib(N-2);
}

斐波那契数列的递归实现方式非常简洁,但简洁一定好吗?那该如何衡量其好与坏呢?

算法的复杂度

算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源等 。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度空间复杂度
时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。

复杂度在校招中的考察

校招中的复杂度考察


时间复杂度

时间复杂度的概念

时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语句的执行次数成正比,虽然每个语句的执行时间不同,相同语句每次的执行时间也不同,但是每次的差距不会很大,语句的执行次数的多少能够大体地反映出一个程序所需要耗费的时间量级(该算法所需的时间在多大的一个时间量级中,一个时间量级能够反映算法运行的一个大概大小的时间区间),算法中的基本操作的执行次数,为算法的时间复杂度
:找到一个算法中基本语句出现次数与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。

// 请计算一下Func1中++count语句总共执行了多少次?
void Func1(int N)
{int count = 0;for (int i = 0; i < N ; ++ i){for (int j = 0; j < N ; ++ j){++count;}}for (int k = 0; k < 2 * N ; ++ k){++count;}int M = 10;while (M--){++count;}printf("%d\n", count);
}

Func1执行的基本操作次数

F ( N ) = N 2 + 2 ∗ N + 10 F(N) = N^2 + 2*N + 10 F(N)=N2+2N+10

  • N = 10 F(N) = 130
  • N = 100 F(N) = 10210
  • N = 1000 F(N) = 1002010
    实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法

大O的渐进表示法

大O符号(Big O notation):是用于描述函数渐进行为的数学符号
推导大O阶方法
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项(比一定狭隘地指谁的次方项最高,因为完全有可能项不是单纯的次方项的初等函数不能化简的表达形式,最高阶项可直接简单地理解成是画函数图象时,谁在正半轴上图象大部分时候比另一函数大,谁就是复杂度大O的表示法当中的“最高次项”,这里的最高次项是一种更加广义的、自由的、开阔的、凭一点感觉的、稍稍没有那么自由的表示方法,这里的命名方法是根据初等函数的种类,以及幂函数的次方项的多少来命名的(这几种情况出现频率最高),当然如果对数的底数不同,以及真数的表达式不同也能够用来命名,但一般这种情况出现的频率少,就不做讨论了,阶的分类根据表达式的初等函数的类型不同以及各初等函数的数据不同是可以分成很多种情况的,一般复杂度的化简规则是都可以又能力将表达式化成一个单一的初等函数的形式的,一般复杂度化简后表达式都会变成单一的一项(不同项同阶的会被合并(初等函数中的某一类型的两项各数据完全相同才叫同阶,不同阶就按上述的简单万能判别方法,谁在上多一些,对整个表达式的结果影响大一些,就会去选择两项或多项中的在输入规模的范围(即各函数的定义域)中居于上部最多分布的那一项去表达(不同的输入规模的范围比较相同的几个不同阶的函数也可能会得到不同结果的最高阶项函数)))乘积形式时就会是两个初等函数相乘,约不掉)。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
使用大O的渐进表示法以后,Func1的时间复杂度为:

O ( N 2 ) O(N^2) O(N2)

  • N = 10 F(N) = 100
  • N = 100 F(N) = 10000
  • N = 1000 F(N) = 1000000
    通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了地表示出了执行次数

4、另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
例如:在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到
在实际中一般情况关注的是算法的最坏运行情况,大O渐进表示法是为确保其准确性表达方式是考虑最坏情况的严谨表示法,所以数组中搜索数据时间复杂度为O(N)

常见时间复杂度计算举例

实例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);
}

实例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);
}

实例3

// 计算Func4的时间复杂度?
void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++ k){++count;}printf("%d\n", count);
}

实例4

// 计算strchr的时间复杂度?
const char * strchr ( const char * str, int character );

实例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;}
}

实例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;
}

实例7

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{if(0 == N)return 1;return Fac(N-1)*N;
}

实例8

// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N)
{if(N < 3)return 1;return Fib(N-1) + Fib(N-2);
}

实例答案及分析

  1. 实例1基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N)
  2. 实例2基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M)
  3. 实例3基本操作执行了10次,通过推导大O阶方法,时间复杂度为 O(1)
  4. 实例4基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为O(N)
  5. 实例5基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N2)
  6. 实例6基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(logN)
    ps:logN在算法分析中表示是底数任意的N的对数,有些地方会写成lgN(数学中的lgN单纯只指底数为2的N的对数,但是在时间复杂度中其代表底数任意的N的对数的logN的缩写形式的缩写,本质是因为计算机中不好写出底数,且时间复杂度本就是估算的形式,所以就可以直接将任意底数的只有一个任意输入规模的输入规模表达式的对数形式全都缩写成logF(N)的形式,方便表达的同时也符合了大O的渐进表达法表达时间复杂度的去掉影响不大的项,简洁明了地表示出执行次数(估算)的特点)。
  7. 实例7通过计算分析发现基本操作递归了N次,时间复杂度为O(N)。
  8. 实例8通过计算分析发现基本操作递归了2N次,时间复杂度为O(2N)。

空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度
空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟时间复杂度类似,一般也直接使用大O的渐进表示法。
注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定(即:空间复杂度会忽略已经在编译时就确定好的函数参数(运行时申请的实参空间),一些早就会确定好的函数运行所必要的局部变量,以及一些函数运行时所必不可少的也是在编译时就确定好的寄存器的大小,而只考虑那些不是在编译时就确定好的必要的内存大小而是运行时才考虑的主要存在与函数体当中的只参与函数运行原理的那些额外创造的空间,可将其简单地理解为就是函数运行时才会创造的函数体当中的那些局部变量。当然一般我们是直接根据算法的思想来确定所开创空间的大小的,而算法思想本质上就是分析的函数运行时的状态,就和只分析函数运行时的函数体中所创造的局部变量的个数的效果本质上是如出一辙的,不管时间还是空间复杂度,只要单纯地根据算法的思想去简单地分析结果就一定会吻合运行时所消耗的时间与开创的额外空间(因为是运行时才开创的,有些甚至情况不同时都不会开创,所以将运行时创建的空间叫作额外空间),结果就一定不会出错)
实例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;}
}

实例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;
}

实例3

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

实例答案及分析

  1. 实例1使用了常数个额外空间,所以空间复杂度为 O(1)
  2. 实例2动态开辟了N个空间,空间复杂度为 O(N)
  3. 实例3递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N)

常见复杂度对比

一般算法常见的复杂度如下:

真实表达式大O的渐进表示法
5201314 5201314 5201314 O ( 1 ) O(1) O(1)常数阶
3 n + 4 3n+4 3n+4 O ( n ) O(n) O(n)线性阶
3 n + 4 n + 5 3^n + 4n + 5 3n+4n+5 O ( n 2 ) O(n^2) O(n2)平方阶
3 l o g 2 n + 4 3log_2n + 4 3log2n+4 O ( l o g n ) O(logn) O(logn)对数阶
2 n + 3 n l o g 2 n + 14 2n + 3nlog_2n + 14 2n+3nlog2n+14 O ( n l o g n ) O(nlogn) O(nlogn)nlogn阶
n 3 + 2 n 2 + 4 n + 6 n^3 + 2n^2 + 4n + 6 n3+2n2+4n+6 O ( n 3 ) O(n^3) O(n3)立方阶
2 n 2^n 2n O ( 2 n ) O(2^n) O(2n)指数阶

大O表达法的各阶函数在0~+无穷时的函数图象


结语

以上就是博主对复杂度的详解,😄希望对你的数据结构的学习有所帮助!看都看到这了,点个小小的赞或者关注一下吧(当然三连也可以~),你的支持就是博主更新最大的动力!让我们一起成长,共同进步!


  1. 计算复杂性理论(Computational complexity theory)是理论计算机科学和数学的一个分支,它致力于将可计算问题根据它们本身的复杂性分类,以及将这些类别联系起来。一个可计算问题被认为是一个原则上可以用计算机解决的问题,亦即这个问题可以用一系列机械的数学步骤解决,例如算法。 ↩︎

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

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

相关文章

面试宝典:深入剖析golang 反射在orm模型中的应用

在 Go 语言中,反射(Reflection)是一种强大的机制,它允许程序在运行时检查和修改自身的结构和行为。在 ORM(Object-Relational Mapping,对象关系映射)模型中,反射被广泛应用于将数据库中的表记录映射到 Go 语言的结构体实例,以及将结构体实例的数据持久化到数据库中。以…

【微服务】OpenFeign+Sentinel集中处理远程调用异常

文章目录 1.微服务基本环境调整1.对10004模块的application.yml调整2.启动nacos以及一个消费者两个提供者3.测试1.输入http://localhost:8848/nacos/index.html 来查看注册情况2.浏览器访问 http://localhost:81/member/nacos/consumer/get/13.结果 2.使用OpenFeign实现微服务模…

EtherCAT主站SOEM -- 22 -- Wireshark抓取并分析EtherCAT数据

EtherCAT主站SOEM -- 22 -- Wireshark抓取并分析EtherCAT数据 0 QT-SOEM视频预览及源代码下载:0.1 QT-SOEM视频预览0.2 QT-SOEM源代码下载1.Wireshark下载及安装2.Wireshark抓取EtherCAT数据2.1 我抓取的数据包3.Wireshark过滤EtherCAT数据3.1 筛选EtherCAT数据的COE数据:3.2…

C# comboBox

在C#中&#xff0c;ComboBox 是一个常用的控件&#xff0c;它允许用户从下拉列表中选择一个项目&#xff0c;或者输入自定义的文本&#xff08;取决于 ComboBox 的 DropDownStyle 属性设置&#xff09;。ComboBox 控件通常用于显示一系列固定的选项&#xff0c;让用户能够快速地…

2024年北京通信展|北京国际信息通信展览会|北京PT展

2024年北京通信展|北京国际信息通信展览会|北京PT展 2024年中国国际信息通信展览会&#xff08;PTEXPO&#xff09;&#xff0c;是由工业和信息化部主办的ICT行业盛会&#xff0c;自1990年创办以来&#xff0c;已成功举办31届&#xff0c;是反映信息通信行业发展最新成果的重要…

Mysql 常用语句及用法记录

一、mysql简介-常用命令&#xff1a; MySQL是一种关系型数据库管理系统&#xff0c;它提供了许多命令和用法来管理和操作数据库。以下是一些常用的MySQL命令及其用法&#xff1a; 1. 连接数据库&#xff1a; mysql -u username -p 用于连接到MySQL服务器&#xff0c;其中usern…

【Java数据结构】关于栈的操作出栈,压栈,中缀表达式,后缀表达式,逆波兰表达式详解

&#x1f525;个人主页&#xff1a;努力学编程’ &#x1f525;内容管理&#xff1a;java数据结构 上一篇文章我们讲过了java数据结构的链表&#xff0c;对于链表我们使用了它的一些基本操作&#xff0c;完成了扑克牌小游戏的操作&#xff0c;如果你感兴趣的话&#xff0c;点…

MATLAB 自定义均值滤波 (53)

MATLAB 自定义均值滤波 (53) 一、算法介绍二、算法实现1.原理2.代码一、算法介绍 均值滤波,是一种常见的点云平滑算法,改善原始点云的数据质量问题,MATLAB自带的工具似乎不太友好,这里提供自定义实现的点云均值滤波算法,具体效果如下所示: 均值滤波前: 均值滤波后:…

Pycharm选择使用Anaconda环境中的Pytorch 失败解决办法之一

前几日想要复现一篇论文&#xff0c;结果给配的台式机完全禁不住&#xff0c;老是报溢出&#xff0c;慢都没事&#xff0c;溢出就很难受了&#xff0c;因此想用自己笔记本的GPU来训练。 安装以后遇到一个问题&#xff1a; Anaconda里创建了环境&#xff0c;安装好了对应pytor…

RPC--远程调用

通信调用 程序A(加密) 程序B 内存共享 (本地RPC) 发送窗口信息 (本地RPC) --长度有限制 串口通讯 com口 --浏览器不开串口... 通讯管道(防止多开) (本地RPC) --对我们不可见. 网络 TCP/IP (远程RPC) --good! 浏览器(, ws) <--- 任意语言开发的软件 --任意语言控制浏览器. 注…

Linux基础知识

文章目录 一、入门命令&#xff1a;1.find 条件 要查找的文件满足的条件&#xff08;从当前目录开始查找&#xff09;&#xff1a;2.locate 文件名&#xff1a;3.lear CTRL L &#xff08;清除终端窗口&#xff09;与cat&#xff08;打印输出文件内容&#xff09;&#xff1a…

CSS3新增的语法(一)

1. CSS3 新增长度单位 rem根元素字体大小的倍数&#xff0c;只与根元素字体大小有关。vw 视口宽度的百分之多少------10vw 就是视口宽度的10% 。vh 视口高度的百分之多少 ------10vh 就是视口高度的10% 。vmax 视口宽高中大的那个的百分之多少。&#xff08;了解即可&#xff…

[TS面试]TS中使用Union Types时注意事项?

TS中使用Union Types时注意事项? 属性和方法的访问? 只能访问共有属性或方法 function getLength(something: string | number):number{return something.length // wrong, 因为number 类型时候没有 .length }function getString(something: string | number):string{retur…

网络性能提升10%,ZStack Edge 云原生超融合基于第四代英特尔®至强®可扩展处理器解决方案发布

随着业务模式的逐渐转变、业务架构逐渐变得复杂&#xff0c;同时容器技术的兴起和逐渐成熟&#xff0c;使得Kubernetes、微服务等新潮技术逐步应用于业务应用系统上。 为了充分释放性能、为业务系统提供更高效的运行环境&#xff0c;ZStack Edge 云原生超融合采用了第四代英特尔…

ROS传感器图像转换

ros通过摄像头来获得图片&#xff0c;传感器数据类型为sensor_msgs中的Image&#xff0c;具体的数据类型组成&#xff1a; sensor_msgs/Image Documentationhttp://docs.ros.org/en/api/sensor_msgs/html/msg/Image.html但是我们一般使用opencv对图像进行处理&#xff0c;所以…

elementui 导航菜单栏和Breadcrumb 面包屑关联

系列文章目录 一、elementui 导航菜单栏和Breadcrumb 面包屑关联 文章目录 系列文章目录前言一、elementui 导航菜单栏和Breadcrumb 面包屑怎么关联&#xff1f;二、实现效果三、实现步骤1.本项目演示布局2.添加面包屑2.实现breadcrumbName方法3.监听方法4.路由指配5.路由配置…

FastAPI+React全栈开发15 让我们构建一个展示API

Chapter03 Getting Started with FastAPI 15 Let’s Build a showcase API FastAPIReact全栈开发15 让我们构建一个展示API REST APIs are all about cycles of HTTP requests and responses, it is the engine that powers the web and is implemented in every web framew…

全国青少年软件编程(Python)等级考试三级考试真题2023年12月——持续更新.....

青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;三级&#xff09; 分数&#xff1a;100 题数&#xff1a;38 一、单选题(共25题&#xff0c;共50分) 1.一个非零的二进制正整数&#xff0c;在其末尾添加两个“0”&#xff0c;则该新数将是原数的&#xf…

具备实时数据更新能力的大语言模型——Larimar

ChatGPT、Claude.ai等大模型产品就像“图书馆”一样为我们生成各种各样的内容。但是想更新这个图书馆里的知识却不太方便&#xff0c;经常需要漫长、费时的预训练、蒸馏才能完成。 研究人员提出了一种具有情景记忆控制的大语言模型Larimar&#xff0c;这是一种类似人脑"海…

love 2d win 下超简单安装方式,学习Lua 中文编程 刚需!!

一、下载love 2d 参考&#xff1a;【Love2d从青铜到王者】第一篇:Love2d入门以及安装教程 或直接下载&#xff1a; 64位&#xff0c;现在一般电脑都可以用。 64-bit zipped 32位&#xff0c;很复古的电脑都可以用。 32-bit zipped 二、解压 下载好了之后&#xff0c;解压到…