时间复杂度介绍及其计算
时间复杂度
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,则去除与这个项目相乘的常数。得到的结果就是大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的渐进表示法,最后就能得到时间复杂度。调用到最后,会形成一个类似等腰三角形的形状。灰色部分是无函数调用,白色部分是函数调用。
4.完结
时间复杂度的内容就到这里啦,若有不足,欢迎评论区指正,下期见!
相关文章:

时间复杂度介绍及其计算
时间复杂度 1.算法效率 如何衡量一个算法的好坏呢?看这段代码: long long Fib(int N) {if(N < 3)return 1;return Fib(N-1) Fib(N-2); }这是斐波那契数列的递归代码,非常简洁,那么这就一定说明它好吗?答案显而易…...

etcd实现大规模服务治理应用实战
导读:服务治理目前越来越被企业建设所重视,特别现在云原生,微服务等各种技术被更多的企业所应用,本文内容是百度小程序团队基于大模型服务治理实战经验的一些总结,同时结合当前较火的分布式开源kv产品etcd,…...

目标检测中 anchor base和anchor free
目标检测中两种不同anchor的生成 趋势:anchor free越来越受到实时性检测的青睐,,,...

TypeC拓展设计方案|TypeC转HDMI设计方案|CS5261/CS5265芯片设计参数对比
集睿智远CS5261/CS5265都可以用于设计TypeC转HDMI方案,低成本TypeC扩展坞设计方案,而两者也有些差异:1.CS5261支持DP1.4输入,一个HDMI1.4输出,即HDMI输出为4K30HZ ;CS5265DP1.4到HDMI2.0转换芯片,即HDMI输出…...

SQL Developer中的Active Data Guard
这篇文章 Display Data Guard configuration in SQL Developer 中,用SQL Developer展示了多种ADG的拓扑。 今天自己也试了一下,还蛮简单的,其实最麻烦的部分在于搭建一个ADG环境。 假设我已有一个ADG环境,即最典型的环境&#x…...

谈谈FFT到底有何用
谈谈FFT到底有何用 FFT快速傅里叶变换是数字信号处理的经典算法,学过DSP或者芯片设计的人大多知道这个算法;但是,大家是否想过,为什么数字信号处理会有那么多FFT呢有人会说,为了分析信号的频谱;那么下边的问题就是,分析频谱对我们的日常需求,比如手机打电话,雷达测量速度和方向…...

MATLAB | 如何绘制这样的描边散点图?
part.-1 前前言 最近略忙可能更新的内容会比较简单,见谅哇,今日更新内容: part.0 前言 看到gzhBYtools科研笔记(推荐大家可以去瞅瞅,有很多有意思的图形的R语言复现!!)做了这样一张图: 感觉很…...

偶数科技与白鲸开源完成兼容性认证
近日,偶数科技自主研发的云原生分布式数据库 OushuDB v5.0 完成了与白鲸开源集成调度工具 WhaleStudio v2.0 的兼容性相互认证测试。 测试结果显示,双方产品相互良好兼容,稳定运行、安全,同时可以满足性能需求,为企业级…...

【机器学习】Feature scaling and Learning Rate (Multi-variable)
Feature scaling and Learning Rate 1、数据集2、学习率2.1 α \alpha α 9.9e-72.2 α \alpha α 9e-72.3 α \alpha α 1e-7 3、特征缩放3.1 特征缩放的原因3.2 Z-score 归一化3.3 预测3.4 损失等值线 导入所需的库 import numpy as np np.set_printoptions(precision…...

windows编译ncnn
官方代码https://github.com/Tencent/ncnn/wiki/how-to-build#build-for-windows-x64-using-visual-studio-community-2017 编译工具 visual studio 2017 一、编译protobuf 1、下载protobuf protobuf-3.11.2:https://github.com/google/protobuf/archive/v3.11…...

C++和Lua交互总结
C和Lua交互总结 Chapter1. C和Lua交互总结一、Lua与C的交互机制——Lua堆栈二、堆栈的操作三、C 调用 Lua1)C获取Lua值2)C调用Lua函数示例: 四、Lua 调用 C包装C函数 最后总结一下 Chapter1. C和Lua交互总结 原文链接:https://bl…...

nvm安装和切换node版本
1、nvm list查看已安装的node版本 2、查看当前使用的npm和node版本 3、安装某版本的node 4、 切换node版本...

每日一题8.2 2536
2536. 子矩阵元素加 1 给你一个正整数 n ,表示最初有一个 n x n 、下标从 0 开始的整数矩阵 mat ,矩阵中填满了 0 。 另给你一个二维整数数组 query 。针对每个查询 query[i] [row1i, col1i, row2i, col2i] ,请你执行下述操作:…...

适配器模式(Adapter)
适配器模式用于将一个接口转换成用户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 Adapter is a structural design pattern that…...

Spring学习笔记——1
Spring学习笔记——1 一、Spring入门1.1、学习路线1.2、传统Javaweb开发困惑及解决方法1.3、三种思想的提出和框架概念1.3.1、IoC、DI和AOP思想提出1.3.2、框架的基本特点 1.4、Spring概述1.5、BeanFactory快速入门1.6、ApplicationContext快速入门1.7、BeanFactory与Applicati…...

leetcode 406. 根据身高重建队列
2023.8.2 这题一开始有点让人懵逼的是有两个维度,一个是身高,还一个是前面人高于自己的人数。这种题一般需要先固定一个维度,再去确定另外一个维度,不要想着兼顾。 经过纸上模拟,我的思路是先通过身高进行从大到小排序…...

Matlab实现AGNES算法
在数据分析和机器学习中,聚类是一种常用的无监督学习方法,它可以将数据点按照某种相似度标准进行分组,从而发现数据中的结构和模式。聚类算法有很多种,其中一种比较经典的是AGNES算法,它是一种基于层次的聚类算法&…...

STM32F4_外部SRAM
目录 前言 1. SRAM控制原理 1.1 SRAM功能框图 1.2 SRAM读写时序 2. FSMC简介 2.1 FSMC架构 2.2 FSMC地址映射 2.3 FSMC控制SRAM时序 3. FSMC结构体 4. 库函数配置FSMC 5. 实验程序 5.1 main.c 5.2 SRAM.c 5.3 SRAM.h 前言 STM32F4自带了192K字节的SRAM࿱…...

Java的代理模式
java有三种代理模式 静态代理 jdk动态代理 cglib实现动态代理 代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的…...

FilterAttributeOnClassMethod
目录 1 BadMethodFilterAttribute 2 FilterAttributeOnClassMethod 2.1 OnMethodExecuted 2.2 OnMethodExecutedAsync 2.3 OnMethodExecuting BadMethodFilterAttribute using System; using System.Threading.Tasks; namespace Flatwhite.Core.Tests.Attributes …...

springboot + (mysql/pgsql) + jpa 多数据源(不同类数据源)
配置文件: spring:datasource:primary:jdbc-url: jdbc:mysql://host:3306/数据库?useUnicodetrue&characterEncodingUTF-8&autoReconnecttrue&failOverReadOnlyfalse&serverTimezoneAsia/Shanghai&zeroDateTimeBehaviorconvertToNullusername…...

【Golang】Golang进阶系列教程--Go 语言 context 都能做什么?
文章目录 前言核心是 Context 接口:包含四个方法:遵循规则WithCancelWithDeadlineWithTimeoutWithValue 前言 很多 Go 项目的源码,在读的过程中会发现一个很常见的参数 ctx,而且基本都是作为函数的第一个参数。 为什么要这么写呢…...

画图干货!14种uml图类型及示例
1. 什么是 UML UML 是统一建模语言的缩写。UML 图是基于 UML(统一建模语言)的图表,目的是直观地表示系统及其主要参与者、角色、动作、工件或类,以便更好地理解、更改、维护或记录信息关于系统。简而言之,UML 是一种…...

计算机视觉实验:人脸识别系统设计
实验内容 设计计算机视觉目标识别系统,与实际应用有关(建议:最终展示形式为带界面可运行的系统),以下内容选择其中一个做。 1. 人脸识别系统设计 (1) 人脸识别系统设计(必做):根据…...

振弦采集仪完整链条的岩土工程隧道安全监测
振弦采集仪完整链条的岩土工程隧道安全监测 隧道工程是一种特殊的地下工程,其建设过程及运行期间,都受到各种内外力的作用,如水压、地震、地质变形、交通荷载等,这些因素都会对隧道的安全性产生影响。因此,对隧道的安…...

NLP实战9:Transformer实战-单词预测
目录 一、定义模型 二、加载数据集 三、初始化实例 四、训练模型 五、评估模型 🍨 本文为[🔗365天深度学习训练营]内部限免文章(版权归 *K同学啊* 所有) 🍖 作者:[K同学啊] 模型结构图: &a…...

使用Vue.js和Rust构建高性能的物联网应用
物联网(IoT)应用是现代技术的重要组成部分,它们可以在各种场景中(例如智能家居,工业自动化等)提供无缝的自动化解决方案。在这篇文章中,我们将探讨如何使用Vue.js和Rust构建高性能的物联网应用。 1. 为什么选择Vue.js…...

idea调节文字大小、日志颜色、git改动信息
idea调节菜单栏文字大小: 调节代码文字大小: 按住ctrl滚动滑轮可以调节代码文字大小: 单击文件即可在主窗口上打开显示: idea在控制台对不同级别的日志打印不同颜色 : “grep console”插件 点击某一行的时候&#x…...

避免大龄程序员边缘化:如何在技术行业中保持竞争力
目录 导语持续学习和进修维护专业形象寻找适合自己的领域构建个人品牌和网络拥抱变化和创新实例结语: 导语 导语:随着科技的不断发展,技术行业的竞争日益激烈。对于那些年龄稍长的程序员来说,如何保持竞争力并避免边缘化成为了一…...

Jenkins工具系列 —— 启动 Jenkins 服务报错
错误显示 apt-get 安装 Jenkins 后,自动启动 Jenkins 服务报错。 排查原因 直接运行jenkins命令 发现具体报错log:Failed to start Jetty或Failed to bind to 0.0.0.0/0.0.0.0:8080或Address already in use 说明:这里提示的是8080端口号…...