数据结构+算法(第03篇):KO!大O——时间复杂度

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

引言

在本系列第1篇《走下神坛吧!算法》中提到了:计算复杂度分为时间复杂度与空间复杂度。本篇文章来讲讲时间复杂度。

如何度量时间复杂度

时间复杂度由所消耗的时间决定。所消耗的时间由硬件与软件共同决定。在同一硬件条件下,所消耗的时间由软件决定。

通常意义上的算法指的是软件算法,所以在谈论时间复杂度时,聚焦软件的时间开销。

软件的时间开销 = 软件各组成部分的时间开销之和。

软件最基本的组成部分是语句(或者微观意义下的指令)。

注:因为在同一硬件条件、特定的编程语言环境下,基本语句由多少条指令构成、运行的模式都是固定的,所以直接以语句作为基本考察单位即可。

整体等于各部分之和

对大部分编程语言而言,基本语句无外乎以下两种:

  1. 读写语句

  2. 比较语句

注:条件语句(if-else)、循环语句(while/for)在这里都不算基本语句,而被看作复合语句。

原因如下:

对于条件语句,一般形式如下:

    // 相当于先运行比较语句// (xxx<->true),// 再根据比较结果运行aaaif (xxx) { aaa;} // 相当于先运行比较语句// (xxx<->true),// 再根据比较结果运行bbbelse if (yyy) { bbb; }

对于循环语句,一般形式如下:

(为简单起见,这里只做while的示例,其他循环类型依此来推)

    // 相当于先运行比较语句(xxx<->true),// 再根据比较结果运行aaa// 然后运行比较语句(xxx<->true),// 判断是否出循环体 while (xxx) { aaa; }

将基本语句的时间开销看作“单位1”,那么整个程序的时间开销就是所有这些“单位1”之和。

推论3.1:

假设程序结构L由n个子结构Li组成,即:

那么运行L的整体时间开销T(L)为:

T(L) = f(n)

来看几个例子:

    // 赋值语句、基本语句,// 时间开销=1 int i = 10; // 比较语句、基本语句,// 时间开销=1a > b; // 条件语句、复合语句// 包含一条比较语句和一条赋值语句// 时间开销=1+1=2if (a > b) { c= 9; }

再来看一个复杂一点的:

    int buf_size = input_array.size;for (i = 1; i < buf_size; i++) { p = q + 1; if (p > i) { break; }}

整个程序结构L由一条赋值语句(记为J)和一个循环体(记为K)组成,

则:L = J + K (式3.1)

循环体K由一条赋值语句X和一个条件语句Y组成,循环次数n在最坏情况下等于buf_size。所以在最坏情况下:

K = n(X + Y) (式3.2)

代入式3.1,得到:L = J + n(X+Y) (式3.3)

根据推论3.1,从式3.3可得到:

T(L) = T(J) + T(n(X + Y)) = T(J) + nT(X + Y) = T(J) + n(T(X) + T(Y)) (式3.4)

因为J, X为赋值语句,属于基本语句,所以T(J) = T (X) = 1

因为Y为简单条件语句,T(Y) = 2

如果buf_size = 10,那么n = 10

T(J), T(X), T(Y), n代入式(3.4)得到:T(L) = 1 + 10 x (1+2) = 31

从上面的例子可以看出:

(i) n不同,T(L)的值就不同;

(ii)n越大,T(L)的值也越大;

(iii) n等于buf_size,后者是由输入input_array的规模决定的

所以,可以看出:T(L)与输入规模相关。用数学的语言来描述就是:整体时间开销是一个关于输入规模因子的函数。

用数学表达式表示就是:T(L) = f(n)

T= f(n)的现实意义是什么

f(n)既然是个函数,那么由高中数学知识可知,f(n)的形式有很多种,如:

不同形式的f(n),在n增加的时候,T增加的速度不同。

每一种f,对应一种算法,所以:

推论3.2:

T表示在同等输入规模(即n一定)的情况下,不同算法的时间开销。

T的增加速度越快,对应算法的时间开销越大,也就表示该算法越复杂。

“两种算法的复杂度在一个量级”到底是个什么鬼

考虑考虑 T1=2n和 T2=n

很显然,在n固定的情况下, T1/T2=2 。即:同等输入规模下,第一种算法的时间开销是第二种算法时间开销的2倍。

这种复杂度关系总是常数倍的,即使n取无穷大也是。用数学语言表示就是:

从而,我们得到一个关键结论:

推论3.3:

两种算法的复杂度在同一量级等价于

其中C是常数

聪明的你肯定会问到:如果n趋向无穷大的时候,T1/T2不是常数呢?

如果不是常数,那么通常是一个关于n的表达式,随着n的增加,值也增加。这表示T1/T2也将趋向无穷大。用数学术语表达就是“发散”。

我们称这种况下,两种算法不在同一复杂度量级。

推论3.4:

算法1比算法2的复杂度量级高等价于

大O登场

通常比较算法复杂度,只用比较量级即可。量级用O()表示。

O()定义:

(i) 如果算法T1与算法T2的复杂度在同一量级,那么O(T1) = O(T2)

(ii) 如果算法T1比算法T2的复杂度量级高,那么O(T1) > O(T2)

(iii) 如果算法T1比算法T2的复杂度量级低,那么O(T1) < O(T2)

比较T1与T2的复杂度量级:

根据推论3.3得到:T1T2处于同一复杂度量级。

根据上述O()的定义:O(T1) = O(T2)

这里其实蕴含了一个非常实用的结论:

推论3.5:

算法复杂度的大O表示可以简化为该算法最高阶部分的复杂度的大O表示。

根据高等数学知识,我们可以得到:

总结

大O表示法最早由德国数论学家保罗·巴赫曼(英语:Paul Bachmann)在其1892年的著作《解析数论》(Analytische Zahlentheorie)首先引入的。

而这个记号则是在另一位德国数论学家艾德蒙·朗道(英语:Edmund Landau)的著作中才推广的,因此它有时又称为朗道符号(Landau symbols)。

大部分的算法或者复杂度理论的书籍,在介绍大O时,要么过于数学形式化,要么过于感性非严格化。

本篇文章旨在用最少的数学知识、启发式行文方式、全新的原创视角,为读者构建一个清晰、严格的时间复杂度概念。

相信看到这里,以后再面对时间复杂度和大O,你一定信心满满了:)

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

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

相关文章

C++进阶--多态

概念 多态是面向对象编程中的一个重要概念&#xff0c;它允许不同类型的对象对同一个消息做出不同的响应。具体的来说&#xff0c;当相同的消息传递给不同的对象时&#xff0c;这些对象能够以不同的方式进行处理&#xff0c;从而产生不同的行为。 对于多态的实现&#xff0c;…

开源MES/免费MES,提升生产效率的最佳选择

开源MES系统为企业提供了一个灵活、经济的选择。企业可以根据自身需求选择合适的开源MES系统&#xff0c;并进行定制开发&#xff0c;可以节约不少成本。开源MES系统的出现&#xff0c;促进了整个制造业的创新和发展&#xff0c;有助于企业提高运营效率和竞争力。今天介绍一款市…

面试中问到的算法题。————目录树生成

前言 我在面试中遇到了算法题&#xff0c;也是我第一次面试&#xff0c;也不知道是太紧张了还是太久没刷算法题了&#xff0c;感觉压有点懵的状态&#xff0c;所以当时面试的时候没有做出来或者说只做了一半没有做完。 面试完成后&#xff0c;我又重新审视了一下题目&#xff…

混乱字母排序——欧拉路数论

题目描述 小明接到一个神秘的任务&#xff1a;对于给定的 n 个没有顺序的字母对&#xff08;无序代表这两个字母可以前后顺序颠倒&#xff0c;区分大小写&#xff09;。请构造一个有 (n1) 个字母的混乱字符串使得每个字母对都在这个字符串中出现。 输入输出格式 输入格式 第…

2023年06月CCF-GESP编程能力等级认证Python编程四级真题解析

Python等级认证GESP(1~6级)全部真题・点这里 一、单选题(共15题,共30分) 第1题 高级语言编写的程序需要经过以下( )操作,可以生成在计算机上运行的可执行代码。 A:编辑 B:保存 C:调试 D:编译 答案:D 第2题 排序算法是稳定的(Stable Sorting),就是指排序算…

iOS pod sdk开发到发布,记录

本文章记录从开发sdk到发布cocopod的问题和流程,省的每次都忘还得重新查 1:pod lib create (sdk名称) 命令创建 工程结构,然后根据命令行提示进行选择. What platform do you want to use?? [ iOS / macOS ]。~》 iOS What language do you want to use?? [ Swift / Obj…

鸿蒙开发有必要学吗?看完这篇再决定吧

在科技的潮流中&#xff0c;每一次新操作系统的诞生都是对旧秩序的挑战与新机遇的孕育。鸿蒙操作系统的出现&#xff0c;无疑是近年来科技界最引人注目的事件之一。自华为于2019年正式推出鸿蒙系统以来&#xff0c;这一我们自主研发的操作系统不仅在国内引起巨大反响&#xff0…

解决ModuleNotFoundError: No module named ‘pysqlite2‘

目录 一、问题描述 二、问题分析 三、解决方法 四、参考文章 一、问题描述&#xff1a; 新建conda编译环境。安装Jupyter后打不开&#xff0c;报错&#xff1a; 二、问题分析&#xff1a; 缺少sqlite3动态链接库 三、解决方法&#xff1a; SQLite Download Page 下载…

力扣461. 汉明距离(位运算)

Problem: 461. 汉明距离 文章目录 题目描述思路复杂度Code 题目描述 思路 Problem: 力扣191. 位1的个数&#xff08;位运算&#xff09; 该题只需要在上题的基础上先对两个数进行一次异或操作即可 复杂度 时间复杂度: O ( 1 ) O(1) O(1) 空间复杂度: O ( 1 ) O(1) O(1) Code …

【LeetCode】每日一题 2024_2_2 石子游戏 VI(排序、贪心)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;石子游戏 VI题目描述代码与解题思路 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 题目&#xff1a;石子游戏 VI 题目链接&#xff1a;1686. 石子游戏 VI 题目描述…

Linux - iptables 防火墙

一. 安全技术和防火墙 1.安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全…

linux 下mongodb7版本怎么连?

概述&#xff1a;linux下的mongodb7版本默认是没有安装客户端的&#xff0c;需要下载shell客户端才能连&#xff0c;下载之后解压&#xff0c;不需要编译&#xff0c;进入bin目录就能自己运行&#xff0c;。 安装&#xff1a; linux 下mongodb7版本没有安装客户端需要当地下载…