数据结构(初阶1.复杂度)

文章目录

一、复杂度概念

二、时间复杂度

  2.1 大O的渐进表示法

    2.2 时间复杂度计算示例

    2.2.1. // 计算Func2的时间复杂度?

    2.2.2.// 计算Func3的时间复杂度?

    2.2.3.// 计算Func4的时间复杂度?

    2.2.4.// 计算strchr的时间复杂度?

     💡 总结

    2.2.5.// 计算BubbleSort (冒泡排序) 的时间复杂度?

    2.2.6.// 计算func5的时间复杂度?

    2.2.7.// 计算阶乘递归Fac的时间复杂度?

三、空间复杂度

  3.1 空间复杂度计算示例

    3.1.1// 计算BubbleSort的空间复杂度?

    3.1.2// 计算阶乘递归Fac的空间复杂度?

四、常见复杂度对比


一、复杂度概念

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

二、时间复杂度

定义:在计算机科学中,算法的时间复杂度是⼀个函数式T(N),它定量描述了该算法的运⾏时                   间,时间复杂度是衡量程序的时间效率,那么为什么不去计算程序的运⾏时间呢?
  1. 因为程序运⾏时间和编译环境和运⾏机器的配置都有关系,⽐如同⼀个算法程序,⽤⼀个⽼编译器进⾏编译和新编译器编译,在同样机器下运⾏时间不同。
  2. 同⼀个算法程序,⽤⼀个⽼低配置机器和新⾼配置机器,运⾏时间也不同。
  3. 并且时间只能程序写好后测试,不能写程序前通过理论思想计算评估。
那么算法的时间复杂度是⼀个函数式T(N)到底是什么呢?这个T(N)函数式计算了程序的执⾏次数。通过c语⾔编译链接章节学习,我们知道算法程序被编译后⽣成⼆进制指令,程序运⾏,就是cpu执⾏这些编译好的指令。那么我们通过程序代码或者理论思想计算出程序的执⾏次数的函数式T(N),
假设每句指令执⾏时间基本⼀样(实际中有差别,但是微乎其微),那么执⾏次数和运⾏时间就是等⽐正相关,这样也脱离了具体的编译运⾏环境。执⾏次数就可以代表程序时间效率的优劣。⽐如解决⼀个问题的算法a程序T(N) = N,算法b程序T(N) = N^2,那么算法a的效率⼀定优于算法b。

看下面代码 

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;}
}
  • 程序时间效率:每条语句运行时间(通过编译环境或者运行环境决定的,存在不确定性)  *  运                             行次数
  • Func1 执⾏的基本操作次数:
             T ( N ) = N 2 + 2 ∗ N + 10
N = 10       N2 = 100    2*N = 20       T(N) = 130
N = 100     N2 = 100    2*N = 200     T(N) = 10210
N = 1000   N2 = 100    2*N = 2000   T(N) = 1002010
通过对N取值分析,对结果影响最⼤的⼀项是 N 2.
实际中我们计算时间复杂度时,计算的也不是程序的精确的执⾏次数,精确执⾏次数计算起来还是很⿇烦的(不同的⼀句程序代码,编译出的指令条数都是不⼀样的),计算出精确的执⾏次数意义也不⼤,因为我么计算时间复杂度只是想⽐较算法程序的增⻓量级,也就是当N不断变⼤时T(N)的差别,上⾯我们已经看到了当N不断变⼤时常数和低阶项对结果的影响很⼩,所以我们只需要计算程序能代表增⻓量级的⼤概执⾏次数,复杂度的表⽰通常使⽤⼤O的渐进表⽰法。

2.1 大O的渐进表示法

⼤O符号(Big O notation):是⽤于描述函数渐进⾏为的数学符号
💡 推导⼤O阶规则
  1.  时间复杂度函数式T(N)中,只保留最⾼阶项,去掉那些低阶项,因为当N不断变⼤时,低阶项对结果影响越来越⼩,当N⽆穷⼤时,就可以忽略不计了。
  2.  如果最⾼阶项存在且不是1,则去除这个项⽬的常数系数,因为当N不断变⼤,这个系数对结果影响越来越⼩,当N⽆穷⼤时,就可以忽略不计了。
  3. T(N) 中如果没有 N 相关的项⽬,只有常数项,⽤常数 1 取代所有加法常数。
    通过以上⽅法,可以得到 Func1 的时间复杂度为: O ( N 2 )
2.2 时间复杂度计算示例
2.2.1. // 计算Func2的时间复杂度?
void Func2(int N)
{int count = 0;for (int k = 0; k < 2 * N ; ++ k){++count;                                  2N}int M = 10;while (M--){++count;                                  10}printf("%d\n", count);
}
  •  Func2执⾏的基本操作次数: F (N) = 2N + 10
  • 根据推导规则第3条得出,Func2的时间复杂度为: O(N)

2.2.2.// 计算Func3的时间复杂度?
void Func3(int N, int M)
{int count = 0;for (int k = 0; k < M; ++ k){++count;                                      M}for (int k = 0; k < N ; ++k){++count;                                      N}printf("%d\n", count);
}
  • Func3执⾏的基本操作次数: F ( N ) = M + N
  • 因此:Func2的时间复杂度为: O ( N )
注意:当  M>>N , O(M);
            当  M>>N , O(M);   

           当  M == N , O(M+N);

2.2.3.// 计算Func4的时间复杂度?
void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++ k){++count;}printf("%d\n", count);
}
  • Func4执⾏的基本操作次数:F (N) = 100
  • 根据推导规则第1条得出Func2的时间复杂度为: O (1)
注意:这里的 1 不是运行一次,而是代表常数。

2.2.4.// 计算strchr的时间复杂度?
const char * strchr ( const char* str, int character)
{const char* p_begin = s;while (*p_begin != character){if (*p_begin == '\0')return NULL;p_begin++;}return p_begin;
}

T(N)取决于查找的位置

strchr执⾏的基本操作次数:
1)若要查找的字符在字符串第⼀个位置,则:         F ( N ) = 1
2)若要查找的字符在字符串最后的⼀个位置,则: F ( N ) = N
3)若要查找的字符在字符串中间位置,则:            F ( N ) = N /2
因此:strchr的时间复杂度分为:
最好情况: O (1)
最坏情况: O ( N )
平均情况: O ( N/2) --> O ( N )
💡 总结
通过上⾯我们会发现,有些算法的时间复杂度存在最好、平均和最坏情况。
最坏情况:任意输⼊规模的最⼤运⾏次数(上界)
平均情况:任意输⼊规模的期望运⾏次数
最好情况:任意输⼊规模的最⼩运⾏次数(下界)
⼤O的渐进表⽰法在实际中⼀般情况关注的是算法的上界,也就是最坏运⾏情况。
2.2.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;}
}

外层循环次数 :    1       2       3       ......    end

内层循环次数 :    N     N-1   N-2     ......     0

BubbleSort执⾏的基本操作次数:
1)若数组有序,则:              F ( N ) = N
2)若数组有序且为降序,则: F ( N) = 1 + 2 + 3+...+ N = N ∗ ( N + 1)/2 -->N2
      因此:BubbleSort的时间复杂度取最差情况为: O ( N 2 )
2.2.6.// 计算func5的时间复杂度?
void func5(int n)
{int cnt = 1;while (cnt < n){cnt *= 2;}
}
当 cnt = 2时,      执⾏次数为1
当 cnt = 4时,      执⾏次数为2
当 cnt = 16时,    执⾏次数为4
若要使该公式不成立,即跳出 while 循环,此时 x 最小为 4
假设执⾏次数为 x ,则 2 x = n
因此执⾏次数: x = log n
因此:func5的时间复杂度取最差情况为: O (log 2 n )
(注意课件中和书籍中 log 2 n log n lg n 的表⽰
当n接近⽆穷⼤时,底数的⼤⼩对结果影响不⼤。因此,⼀般情况下不管底数是多少都可以省略不
写,即可以表⽰为 log n
不同书籍的表⽰⽅式不同,以上写法差别不⼤,我们建议使⽤ log n)
2.2.7.// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{if(0 == N)return 1;return Fac(N-1)*N;
}

                     递归过程: Fac(n) ->  Fac(n-1) ->  Fac(n-2) -> ...  Fac(0) 

单次递归的时间复杂度: O(1)           O(1)            O(1)           ...   O(1)

递归的次数为N;

时间复杂度:单次递归的时间复杂度 * 递归次数:O(1)  * N = O(N)

阶乘递归的时间复杂度为: O (N )

三、空间复杂度

空间复杂度也是⼀个数学表达式,是对⼀个算法在运⾏过程中因为算法的需要额外临时开辟的空间。
空间复杂度不是程序占⽤了多少bytes的空间,因为常规情况每个对象⼤⼩差异不会很⼤,所以空间复杂度算的是变量的个数。
空间复杂度计算规则基本跟实践复杂度类似,也使⽤⼤O渐进表⽰法。
注意:函数运⾏时所需要的栈空间(存储参数、局部变量、⼀些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运⾏时候显式申请的额外空间来确定

3.1 空间复杂度计算示例

3.1.1// 计算BubbleSort的空间复杂度?

在 2.2.5 中我们计算了 BubbleSort 的时间复杂度,那么空间复杂度又是怎么计算呢?

void BubbleSort(int* a, int n)
{assert(a);  for (size_t end = n; end > 0; --end)  //  (size_t end = n)   申请一次{    int exchange = 0;                     //  (int exchange = 0) 申请一次for (size_t i = 1; i < end; ++i)  //  (size_t i)         申请一次  {if (a[i-1] > a[i]){Swap(&a[i-1], &a[i]);exchange = 1;}}if (exchange == 0)   //数组有序break;}
}
函数栈帧在编译期间已经确定好了, 只需要关注函数在运⾏时额外申请的空间。
BubbleSort额外申请的空间有 exchange、  size_t end  、size_t i 等 有限个局部变量,使⽤了常数个额外空间,因此空间复杂度为 O (1)。
3.1.2// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{if(0 == N)return 1;return Fac(N-1)*N;
}
Fac递归调⽤了N次,额外开辟了N个函数栈帧,每个栈帧使⽤了常数个空间
因此空间复杂度为: O ( N )

四、常见复杂度对比

完结~~

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

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

相关文章

构造者模式的实现

引言——构造复杂对象的艺术 软件工程中&#xff0c;构造复杂对象的艺术被巧妙地封装在构造者模式&#xff08;Builder Pattern&#xff09;中。这种设计模式不仅提供了一种清晰且灵活的方式来构建复杂对象&#xff0c;还使得代码更具可读性和可维护性。构造者模式的核心思想是…

Unity最新第三方开源插件《Stateful Component》管理中大型项目MonoBehaviour各种序列化字段 ,的高级解决方案

上文提到了UIState, ObjectRefactor等,还提到了远古的NGUI, KBEngine-UI等 这个算是比较新的解决方法吧,但是抽象出来,问题还是这些个问题 所以你就说做游戏是不是先要解决这些问题? 而不是高大上的UiImage,DoozyUI等 Mono管理引用基本用法 ① 添加Stateful Component …

安全测试理论

安全测试理论 什么是安全测试&#xff1f; 安全测试&#xff1a;发现系统安全隐患的过程安全测试与传统测试区别 传统测试&#xff1a;发现bug为目的 安全测试&#xff1a;发现系统安全隐患什么是渗透测试 渗透测试&#xff1a;已成功入侵系统为目标的的攻击过程渗透测试与安全…

“好物”推荐+Xshell连接实例+使用Conda创建独立的Python环境

目录 主题&#xff1a;好易智算平台推荐RTX 4090DGPU实例租用演示安装配置torch1.9.1cuda11.1.1环境引言&#xff1a;算力的新时代平台介绍&#xff1a;技术与信任的结晶使用案例&#xff1a;实际使用展示创建实例开始使用连接实例&#xff08;下文演示使用Xshell连接&#xff…

昇思25天学习打卡营第二十天|基于MobileNetv2的垃圾分类

打卡营第二十天&#xff0c;今天学习的内容是MobileNet垃圾分类&#xff0c;记录一下学习内容&#xff1a; 学习内容 本文档主要介绍垃圾分类代码开发的方法。通过读取本地图像数据作为输入&#xff0c;对图像中的垃圾物体进行检测&#xff0c;并且将检测结果图片保存到文件中…

【ARM】CCI集成指导整理

目录 1.CCI集成流程 2.CCI功能集成指导 2.1CCI结构框图解释 Request concentrator Transaction tracker Read-data Network Write-data Network B-response Network 2.2 接口注意项 记录一下CCI500的ACE slave interface不支持的功能&#xff1a; 对于ACE-Lite slav…

基于信号处理的PPG信号滤波降噪方法(MATLAB)

光电容积脉搏波PPG信号结合相关算法可以用于人体生理参数检测&#xff0c;如血压、血氧饱和度等&#xff0c;但采集过程中极易受到噪声干扰&#xff0c;对于血压、血氧饱和度测量的准确性造成影响。随着当今社会医疗保健技术的发展&#xff0c;可穿戴监测设备对于PPG信号的质量…

简单的SQL字符型注入

目录 注入类型 判断字段数 确定回显点 查找数据库名 查找数据库表名 查询字段名 获取想要的数据 以sqli-labs靶场上的简单SQL注入为例 注入类型 判断是数字类型还是字符类型 常见的闭合方式 ?id1、?id1"、?id1)、?id1")等&#xff0c;大多都是单引号…

【ASTGCN】模型调试学习笔记--数据生成详解(超详细)

利用滑动窗口生成时间序列 原理图示&#xff1a; 以PEMS04数据集为例。 该数据集维度为&#xff1a;(16992,307,3)&#xff0c;16992表示时间序列的长度&#xff0c;307为探测器个数&#xff0c;即图的顶点个数&#xff0c;3为特征数&#xff0c;即流量&#xff0c;速度、平…

期权专题12:期权保证金和期权盈亏

目录 1. 期权保证金 1.1 计算逻辑 1.2 代码复现 1.3 实际案例 2. 期权盈亏 2.1 价格走势 2.2 计算公式 2.2.1 卖出期权 2.2.2 买入期权 免责声明&#xff1a;本文由作者参考相关资料&#xff0c;并结合自身实践和思考独立完成&#xff0c;对全文内容的准确性、完整性或…

[CISCN 2023 华北]normal_snake

[CISCN 2023 华北]normal_snake 源码和依赖 算了直接说吧&#xff0c;不想截图了&#xff0c;就多了一个C3P0和yaml的依赖 然后read路由可以反序列化yaml的Str 我们看到waf 那个String是可以二次反序列化绕过的,然后CUSTOM_STRING1解码后是"BadAttributeValuePairExcept…

【java】力扣 反转链表

力扣 206 链表反转 题目介绍 解法讲解 先定义两个游标indexnull&#xff0c;prenull&#xff0c;反转之后链表应该是5&#xff0c;4&#xff0c;3&#xff0c;2&#xff0c;1&#xff0c;我们先进行2->1的反转&#xff0c;然后再循坏即可 让定义的游标index去存储head.n…

MySQL设置白名单限制

白名单&#xff08;Whitelist&#xff09;是一种机制&#xff0c;用于限制哪些主机可以连接到服务器&#xff0c;而阻止其他主机的访问。通过配置白名单&#xff0c;可以增加服务器的安全性&#xff0c;防止未授权的访问。 在MySQL数据库中直接设置白名单访问&#xff08;即限制…

【触摸屏】【地震知识宣传系统】功能模块:视频 + 知识问答

项目背景 鉴于地震知识的普及对于提升公众防灾减灾意识的重要性&#xff0c;客户希望开发一套互动性强、易于理解的地震学习系统&#xff0c;面向公众、学生及专业人员进行地震知识教育与应急技能培训。 产品功能 系统风格&#xff1a;严谨的设计风格和准确的信息呈现&#…

红酒的艺术之旅:品味、鉴赏与生活的整合

在繁忙的都市生活中&#xff0c;红酒如同一道不同的风景线&#xff0c;将品味、鉴赏与日常生活巧妙地整合在一起。它不仅仅是一种饮品&#xff0c;更是一种艺术&#xff0c;一种生活的态度。今天&#xff0c;就让我们一起踏上这趟红酒的艺术之旅&#xff0c;探寻雷盛红酒如何以…

【qt】如何读取文件并拆分信息?

需要用到QTextStream类 还有QFile类 对于文件的读取操作我们可以统一记下如下操作: 就这三板斧 获取到文件名用文件名初始化文件对象用文件对象初始化文本流 接下来就是打开文件了 用open()来打开文件 用readLine()来读取行数据 用atEnd()来判断是否读到结尾 用split()来获取…

02. Hibernate 初体验之持久化对象

1. 前言 本节课程让我们一起体验 Hibernate 的魅力&#xff01;编写第一个基于 Hibernate 的实例程序。 在本节课程中&#xff0c;你将学到 &#xff1a; Hibernate 的版本发展史&#xff1b;持久化对象的特点。 为了更好地讲解这个内容&#xff0c;这个初体验案例分上下 2…

go-高效处理应用程序数据

一、背景 大型的应用程序为了后期的排障、运营等&#xff0c;会将一些请求、日志、性能指标等数据保存到存储系统中。为了满足这些需求&#xff0c;我们需要进行数据采集&#xff0c;将数据高效的传输到存储系统 二、问题 采集服务仅仅针对某个需求开发&#xff0c;需要修改…

防火墙小试——部分(书接上回)

toop接上回 1.实验拓扑及要求 前情回顾 DMZ区内的服务器&#xff0c;办公区仅能在办公时间内&#xff08;9&#xff1a;00 - 18&#xff1a;00&#xff09;可以访问&#xff0c;生产区的设备全天可以访问. 生产区不允许访问互联网&#xff0c;办公区和游客区允许访问互联网 …

C#统一委托Func与Action

C#在System命名空间下提供两个委托Action和Func&#xff0c;这两个委托最多提供16个参数&#xff0c;基本上可以满足所有自定义事件所需的委托类型。几乎所有的 事件 都可以使用这两个内置的委托Action和Func进行处理。 Action委托&#xff1a; Action定义提供0~16个参数&…