时间复杂度介绍及其计算

时间复杂度

1.算法效率

如何衡量一个算法的好坏呢?看这段代码:

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

这是斐波那契数列的递归代码,非常简洁,那么这就一定说明它好吗?答案显而易见。

2.算法的复杂度

算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏,一般是从时间空间两个维度来衡量的,即时间复杂度空间复杂度

==时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。==在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。

3.算法的时间复杂度

概念:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,就是算法的时间复杂度。

也就是:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。

Test01

//计算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);
}

count++语句的执行次数:F(N) = N^2 + 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. 在修改后的运行函数次数中,只保留最高阶项。
  3. 如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

那么在Test01中,使用大O表示法之后:时间复杂度为 O(N^2)

大O表示法实际上是去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。

另外有些算法的时间复杂度存在最好、平均和最坏情况:

最坏情况:任意输入规模的最大运行次数(上界)

平均情况:任意输入规模的期望运行次数

最好情况:任意输入规模的最小运行次数(下界)

例如:在一个不重复长度为N的数组中寻找一个值为x的下标。

最好情况:1次找到

最坏情况:N次找到

平均情况:N/2次找到

但是在实际操作中,一般关注的是算法的最坏运行情况所以数组中搜索数据时间复杂度为O(N)

Test02:

//计算Func2中的++count语句执行了多少次?
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);
}

Test02的时间复杂度用大O的渐进表示法就为:O(N)。

原因解释:这里的++count语句严格计算的话:共执行了:2N+10次,但是根据大O的渐进表示规则:最高阶项是2N,这里将2去掉,剩下的部分就是时间复杂度。

Test03:

//计算Func3中的++count语句执行了多少次?
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);
}

Test03的时间复杂度为:

  • 若N与M接近:则O(N)或者O(M)都可以。(当N与M接近时,++count语句的执行次数接近于2N或2M,再去掉常数部分,即可得到答案)
  • 若M>>N,则为O(M)。(当M>>N时,N就可忽略不计。)
  • 若N>>M,则为O(N)。

Test04:

void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++k){++count;}printf("%d\n", count);
}

Test04的时间复杂度为:O(1)

解释:无论其他变量如何变化,++count语句始终会执行100次,始终为常数次,时间复杂度用大O的渐进表示法则为:O(1)。

Test05:

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

strchr函数的功能是:在str字符串中寻找character字符的下标,若不存在则返回-1。这个函数查找的可以分为最好和最坏两种情况:

  • 最好情况:1次就找到
  • 最坏情况:搜完整个字符串才找到或者不存在。

而在大O的渐进表示法中,一般表示最坏的情况,假设字符串的长度为N,那么strchr函数的时间复杂度就是O(N)了。

Test06:

// 计算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;}
}

Test06的时间复杂度为:O(N2).

原因:冒泡排序是由两层循环嵌套实现的,数组长度为n。假设最坏情况:数组中的元素由大到小排列。外层循环要执行n-1次,内层循环会随着外层循环的增加而减少,所以整体的执行次数为:(N-1) + (N-2) + (N-3) + (N-4) + ……+1,这是一串等差数列,最高阶项就是N2,所以时间复杂度也就是O(N2)。

Test07:

// 计算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;
}

Test07的时间复杂度是:O(logN),(以2为底,N的对数)。

解释:考虑最差情况:要寻找的数在边界上,即二分区间之内只有一个数。一个长度为N的数组,要执行多少次二分,才能让二分区间只有一个数字?答案是logN次。所以时间复杂度就为O(logN)。

二分查找的效率是非常高的,但是由于被二分的数组必须有序,那么二分查找才能有效执行,这就导致了二分查找是不经常使用的。

注意:logN这种写法,如无特殊说明,底数都是2.

Test08:

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

Test08的时间复杂度为:O(N)

解释:Fac是一个用于计算阶层的函数,这里的递归次数取决于参数N。递归调用的复杂度就是多次调用次数的累加,而在每一次的递归调用中,语句的执行次数为常数次,也就是O(1),这里的时间复杂度就是函数的调用次数了,也就是O(N)。

Test09:

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

Test09的时间复杂度为:O(N2)

解释:函数每调用一次,就会再次向下调用两次这个函数,直到一方调用到F(2)或F(1)为止。如图所示,如图的每一层,函数调用次数会随着函数调用深度而增加,由20 到 21 ,22,直到2 N-1 ,再对这些调用次数进行相加,再使用大O的渐进表示法,最后就能得到时间复杂度。调用到最后,会形成一个类似等腰三角形的形状。灰色部分是无函数调用,白色部分是函数调用。

> [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bn2iJWH4-1690516905323)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230728115653304.png)]

> [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cf44B8WL-1690516905324)(C:\Users\30539\AppData\Roaming\Typora\typora-user-images\image-20230728115613047.png)]

4.完结

时间复杂度的内容就到这里啦,若有不足,欢迎评论区指正,下期见!

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

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

相关文章

Stable Diffusion - 扩展 SegmentAnything 和 GroundingDINO 实例分割算法 插件的配置与使用

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/131918652 Paper and GitHub&#xff1a; Segment Anything: SAM - Segment Anything GitHub: https://github.com/facebookresearch/s…

【源码解析】SpringBoot循环依赖源码解析II

前言 前面已经写过循环以来的分析&#xff0c;对循环依赖有一些了解&#xff0c;【源码解析】Spring循环依赖和三级缓存。简单回顾一下&#xff1a; Spring可以解决Autowired注入的循环依赖 Spring解决不了构造器注入的循环依赖 使用Aysnc注解会导致循环依赖。提前暴露的Bea…

LeetCode 刷题 数据结构 数组 485 最大连续1的个数

给定一个二进制数组 nums &#xff0c; 计算其中最大连续 1 的个数。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,0,1,1,1] 输出&#xff1a;3 解释&#xff1a;开头的两位和最后的三位都是连续 1 &#xff0c;所以最大连续 1 的个数是 3.示例 2: 输入&#xff1a;nums […

C语言中的函数(超详细)

C语言中的函数&#xff08;超详细&#xff09; 一、函数概述二、C语言中函数的分类1.库函数2.自定义函数三、函数的参数1.实际参数&#xff08;实参&#xff09;2.形式参数&#xff08;形参&#xff09;四、函数的调用1.传值调用2.传址调用五、函数的嵌套调用和链式访问1.嵌套调…

大于号在python中怎么打,python大于等于怎么写

大家好&#xff0c;小编为大家解答python中大于并小于一个数代码的问题。很多人还不知道python中大于等于且小于等于&#xff0c;现在让我们一起来看看吧&#xff01; 1、python 中不等于怎么表示 #!/usr/bin/python a1 b2 if ab: print "a 等于 b" if a!b: print &…

【playbook】Ansible的脚本----playbook剧本

Ansible的脚本----playbook剧本 1.playbook剧本组成2.playbook剧本实战演练2.1 实战演练一&#xff1a;给被管理主机安装Apache服务2.2 实战演练二&#xff1a;使用sudo命令将远程主机的普通用户提权为root用户2.3 实战演练三&#xff1a;when条件判断指定的IP地址2.4 实战演练…

图文教程:如何在 3DS Max 中创建3D迷你卡通房屋

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 在本教程中&#xff0c;我们将学习如何创建一个有趣的、低多边形的迷你动画房子&#xff0c;你可以在自己的插图或视频游戏项目中使用它。您将学习的一些技能将包括创建基本的3D形状和基本的建模技术。让我…

IP网络对讲求助模块

SV-6002 IP网络对讲求助模块是一款壁挂式一键求助对讲模块&#xff0c;具有10/100M以太网接口&#xff0c;其接收网络的音频数据&#xff0c;实时解码播放&#xff0c;还配置了麦克风输入和扬声器功放输出。SV-6002模块可实现对讲、广播、等功能&#xff0c;作为网络广播对讲系…

Docker Compose 容器编排 + Docker--harbor私有仓库部署与管理

目录 一、Docker Compose简介 1、Docker Compose 的YAML 文件格式及编写注意事项 2、Docker compose 使用的三个步骤 3、 Docker Compose配置常用字段 4、 Docker Compose 常用命令 5、 Docker Compose 文件结构 二&#xff1a; Docker Compose 安装 1、Docker Compose…

FreeRTOS之互斥量

什么是互斥量&#xff1f; 在多数情况下&#xff0c;互斥型信号量和二值型信号量非常相似&#xff0c;但是从功能上二值型信号量用于同步&#xff0c; 而互斥型信号量用于资源保护。 互斥型信号量和二值型信号量还有一个最大的区别&#xff0c;互斥型信号量可以有效解决优先级…

合作客户销售数据可视化分析

以一个案例进行实际分析&#xff1a; 数据来源&#xff1a;【地区数据分析】 以此数据来制作报表。 技巧一&#xff1a;词云图 以城市名称来显示合同金额的分布&#xff0c;合同金额越大&#xff0c;则城市文字显示越大。 技巧二&#xff1a;饼图 下面制定一个&#xff0c;合…

热备份路由协议原理

热备份路由协议原理 HSRP协议/VRRP协议热备份协议 热备份协议&#xff08;Hot Standby Protocol&#xff09; 是一种基于冗余设计的协议&#xff0c;用于提高网络的可靠性和冗余性。它允许多个设备共享同一个IP地址&#xff0c;其中一个设备被选为主设备&#xff0c;其他设备…

Java 源码打包 降低jar大小

这里写目录标题 Idea maven 插件配置pom.xml 配置启动技巧 Idea maven 插件配置 pom.xml 配置 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><!-- 只…

React之生命周期

React之生命周期 旧版本&#xff0c;函数组件是没有生命周期的。新版本中通过useEffect触发函数的生命周期 一、基于类组件的生命周期 React的组件生命周期分为挂载阶段、更新阶段和销毁阶段。因为React的state不具有Vue的响应式&#xff0c;所以并没有create阶段 1、挂载阶段&…

不同路径 II

一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上角到右下角…

LBERT论文详解

论文地址&#xff1a;https://arxiv.org/abs/2105.07148 代码地址&#xff1a;https://github.com/liuwei1206/LEBERT 模型创新 LEBRT采用句子中的词语对&#xff08;论文中称为Char-Word Pair&#xff09;的特征作为输入作者设计Lexicon adapter&#xff0c;在BERT的中间某一…

图形编辑器开发:是否要像 Figma 一样上 wasm

大家好&#xff0c;我是前端西瓜哥。 wasm 拿来做 Web 端的图形编辑器貌似是不错的选择。 因为图形处理会有相当多无法利用到 WebGL GPU 加速的 CPU 密集的计算。比如对一条复杂贝塞尔曲线进行三角化&#xff0c;对多个图形进行复杂图形的布尔运算。 图形编辑器性能天花板 F…

从娱乐产品到效率工具,ARknovv首款AR眼镜回归“AR本质”

如果说2022年是AR的元年&#xff0c;2023年则有望成为消费级AR眼镜的新拐点。 今年AR眼镜行业发展明显加快&#xff0c;且不断有大厂入局&#xff1a;今年2月小米发布无线AR眼镜探索版&#xff1b;3月荣耀也推出了一款全新的观影眼镜&#xff1b;而苹果在6月发布的MR头显Visio…

【计算机视觉中的 GAN 】 - 生成学习简介(1)

一、说明 在阅读本文之前&#xff0c;强烈建议先阅读预备知识&#xff0c;否则缺乏必要的推理基础。本文是相同理论GAN原理的具体化范例&#xff0c;阅读后有两个好处&#xff1a;1 巩固了已经建立的GAN基本概念 2 对具体应用的过程和套路进行常识学习&#xff0c;这种练习题一…

transformer理解

transformer的理解 Q、K、V的理解 核心是自注意力机制。即每个位置的结果为所有位置的加权平均和。为了得到每个位置的权重,需要Q*K得到。 整个多头的self-attention过程 单个encoder encoder-decoder encoder中的K和V会传到decoder中的encoder-decoder attention中。 …