算法

2023-10-26

算法的效率

算法的效率主要由以下两个复杂度来评估:

时间复杂度:评估执行程序所需要的时间。可以估算出程序对处理器的使用程度

空间复杂度:评估执行程序所需要的的存储空间。可以估算出程序对计算机内存的使用程度

设计算法时,一般要先考虑系统环境,然后在权衡时间复杂度和空间复杂度,选取一个平衡点。不过,时间复杂度要比空间复杂度更容易产生问题,因此算法主要研究的也是时间复杂度,不说明的情况下,复杂度指的就是时间复杂度。


时间复杂度

下面看一个小栗子:

fun fun1(): Int {
    println("哈,我是345")
    return 0;
}


fun fun2(n: Int): Int {
    for (i in 0..n) {
        println("哈,我是345  ")
    }

    return 0
}

首先,调用一次 fun1,内部会执行两次语句,这个两次可以记做为常数

调用一次 fun 2,内部会执行 1 + (n+1)+n +n +1,也就是 3n +3次,为啥呢,如下:

1,首先 i = 1是一次

2,i 每次会判断是否小于 n ,会执行 n+1 次

3,i 每次循环会自增,则执行 n 次

4,打印语句会执行 n 次

5,return 一次

最终的结果为 3n+3次


所以上面的两个函数的执行次数分别是 2 和 3n+3 次。

但是日常开发中不可能这样去数,所以根据代码执行时间和执行过程就有一个规律,也就是代码执行的时间 T(n) 和代码的执行次数 f(n) ,这个是成正比的,而且这个规律有一个公式 :
T ( n ) = O ( f ( n ) ) T(n) = O(f(n)) T(n)=O(f(n))
其中,n 是输入数据的大小或者输入数据的数量

T(n) :表示一段代码的总执行时间

f (n):表示一段代码的执行总次数

0 :表示代码的执行时间 T(n) 和执行次数 f(n) 成正比

完整的公式看着有些麻烦,我们平时的算法复杂度主要是用 0() 来表示,读作大欧表示法。


回到上面的两个例子:

  • 第一个函数执行了三次,用复杂度表示就是 O(3)
  • 第二个函数执行了 3n +3 次,复杂度就是 O(3n + 3)

上面的写法还是非常麻烦,如果函数的内部的执行次数是固定的,并没有变量存在,那么他的执行时间也就没有什么差别了,例如 O(3) 和 O(4) 。所以复杂度中有一个同意的简化方法,这个简化方法的估算就是我们最终的时间复杂度,简化过程如下:

  • 如果 T(n) = 常数

    那么时间复杂度可以估算为 1 ,因此 T(n) = 2 的复杂度就是 1

    对于 fun1() 函数,他的执行次数是一个常数,也就是说他的数量是固定的,那么他的时间复杂度就是 O(1),就算是它内部有上万行代码,时间复杂度也是 0(1)

  • 如果 T(n) = 常数 * n + 常数

    可以直接 去掉后面的 + 常数,应为当 (常数 * n) 越来越大,后面的则是保持不变,所以后面的相当于不存在,所以可以直接省略

    然后 (常数 * n) 中的常数可以估算为 1,也可以理解为去掉这个作为系数的常数,所以他的时间复杂度就是 0(n)

    对于 fun2() 函数,他的执行次数是 常数 *n ,那么他的时间复杂度就是 0(n)

    因此 T(n) = 3n + 3 的复杂度为 n

  • T(n) = 多项式(5n³ + 6666n² + 233)

    对于多项式,我们只需要保留 n 的最高次项,也就是保留 n 的次方数最大的那一项,低于这个次项的直接忽略不计。因此 时间复杂度就是 0(n³)

常见的时间复杂度

  • O(1):常数阶

    fun fun1(): Int {
        println("哈,我是345")
        println("哈,我是345")
        println("哈,我是345")
        return 0;
    }
    

    只要算法中没有循环或者递归,不管多少行代码,他的复杂度都是 0(1)

  • O(n):线性阶

    fun fun2(n: Int): Int {
        for (i in 0..n) {
            println("哈,我是345  ")
        }
     		for (i in 1..n) {
            println("哈,我是345  ")
        }
        return 0
    }
    

    虽然上面有两个循环,但是并没有嵌套,所以时间复杂度就是 0(n)

  • O(n²):平方阶

    fun fun2(n: Int): Int {
       for (i in 0..n) {
           for (i in 1..n) {
           	 println("哈,我是345  ")
       		 }
        }
       for (i in 1..n) {
           	 println("哈,我是345  ")
       	}
        return 0
    }
    
  • O(logn):对数阶

    最经典的例子就是二分查找了,每次都从剩下的一半中进行查找,这就是典型的 logn。因为循环的次数影响的主要来源就是 n / 2, 这个时间复杂度就是 O(logn)。

    fun foo3(n:Int){
        for(let i = 1; i < n; i *= 2){
            console.log("一天")
        }
    }
    

    循环执行的次受到了 i *= 2 影响,乘完后距离 n 就越来越近了。该例子中:

    真数,也就是 n 的大小

    底数就是值变化的规律,例如这里每次循环都是 i *= 2 ,这个乘2就是规律。例如 1,2,3,4,5 这样的话,底数就是 1,每个数的规律变化都是 +1

    对数可以理解为 *2 乘了多少次的次数。

    所以,这个题中的 底数就是 2,对数就是 b,结果就是 n ,表达式如下:

    a^b = n ,读作以 a 为底 n 的对数等于 b,也就是 2^b = n,读作以2为底n的对数。转换一下,就是 2^b = n

    所以这个题就可以理解为 log2n = ?,用时间复杂度就可以表示为 O(log2n),由于时间复杂度需要去掉系数和常数所以这个时间复杂度就是 O(logn),

其他时间复杂度

复杂度 名称
0(1) 常数复杂度
0(logn) 对数复杂度
O(n) 线性复杂度
O(n²) 平方
0(n³) 立方
0(2^n) 指数
O(n!) 阶乘

以上复杂度都是由快到慢排列

未标题-3.jpg

以上数据图片参自https://juejin.cn/post/6999307229752983582

空间复杂度

空间复杂度就是算法需要多少内存,占用了多少空间,常用的空间复杂度有 O(1) ,O(n) ,O(n²)

  • O(1)

    fun fun1(): Int {
        println("哈,我是345")
        println("哈,我是345")
        println("哈,我是345")
        return 0;
    }
    

    只要不是因为算法的运行而额外的导致空间增长,内部无论多少行代码,空间复杂度都是 O(1)

  • O(n)

    fun fun2(n: Int) {
        val list = arrayListOf<Int>()
        for (i in 0..n) {
            list.add(i)
        }
    }
    

    如上面这种,n 的数值越大,所需要分配的空间就越多,所以他的空间复杂度是 O(n) ,时间复杂度也是 O(n)

  • O(n²)

    fun fun2(n:Int){
        var list = ArrayList<ArrayList<Int>>()
    }
    

    这种复杂度一般出现在二位数组,或者是矩阵的情况下。

最后

因为最近在刷一些算法题,恰好对这些有点陌生,所以就复习了一下。

参考内容

另外,最近一直在刷一些算法题,有兴趣的同学可以一起来刷,每天一到两个小算法,也算是消磨一下时间。地址是:*https://github.com/Blue-knife/Tortoise *,直接看 issus 即可。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

算法 的相关文章

随机推荐

  • 论文阅读技巧之三遍法

    本文介绍了三遍法及其在文献调查中的应用 关键的思想是 你应该以三遍的时间来阅读论文 而不是从一开始就一直读到最后 每个pass都实现了特定的目标 并建立在前一个遍的基础上 第一个pass让您对本文有一个大致的了解 第二步让你掌握论文的内容
  • es查询对应索引下的数据结果

    执行语句 GET test index mapping pretty test index 索引名称 mapping 查询索引的结构 pretty 是参数 意思是格式化数据
  • spring手动开启事务,手动提交事务,手动回滚事务

    1 未加事务注解 或者事务配置 所以需要手动开启事务和手动提交事务和手动回滚事务 Autowired private PlatformTransactionManager txManager Autowired private ShopGr
  • Vue中表单手机号验证与手机号归属地查询

    下面是一篇关于Vue中如何进行表单手机号验证与手机号归属地查询的Markdown格式的文章 包含代码示例 Vue中表单手机号验证与手机号归属地查询 手机号验证和归属地查询是许多Web应用程序中常见的功能之一 在Vue js中 我们可以轻松地
  • 安装npm和cnpm

    一 简介 npm是nodejs的包管理工具 用于node插件管理 cnpm是淘宝在中国做的nodejs镜像 避免访问国外的nodejs网站出现异常 二 安装nodejs 1 安装 有两种选择一种是安装文件安装 一种是免安装的zip包 这里我
  • 使用Rust写操作系统(1)-安装rust开发环境

    安装cargo及rust编译环境 sudo curl https sh rustup rs sSf sh 如图 选择自定义安装 在版本选择的时候 一定要选择nightly 因为开发操作系统要使用到一些非稳定版本的功能 选择完成后 继续安装即
  • Java序列化详解

    序列化是一种将对象转换成字节流的过程 以便在网络上传输或将其保存到磁盘上 Java提供了一种称为Java序列化的机制 它可以将Java对象转换成字节流 并在需要时将其还原回对象 在本文中 我们将介绍Java序列化的使用方法 并提供一些示例代
  • 都2021了作为一名Android开发者,还不学音视频开发?我劝你早点认清现实!

    缘起 最近经常遇到一些同学问我如何学习音视频 怎样才能快速上手 还有一些对音视频不了解的同学问我该不该学习音视频 作为一名音视频行业的10年Android老兵 我有一些思考分享给大家 希望能对你有所帮助 大趋势 从未来的大趋势来看 随着5G
  • 【C语言位运算符及原码输出】

    C语言位运算符及原码输出 原码 补码 反码基础概念 按位与 按位或 按位异或 按位与 lt lt 按位左移 gt gt 按位右移 位运算符注意事项 两个操作数均以补码参与计算 得到的结果为补码 需将结果转为原码才是最终答案 原码 补码 反码
  • element-plus 提示找不到名称“ElMessage”。ts(2304)

    文章目录 1 安装element plus 2 main ts 引入ElMessage 3 vite config ts 中配置 4 在vscode中使用会报错 找不到名称 ElMessage ts 2304 1 安装element plu
  • umi使用mock

    引入 Mock import Request Response from umijs deps compiled express import Mock from mockjs 定义数据类型 export default GET api t
  • 微信小程序vant组件库安装

    vant组件库安装步骤 1 通过npm安装 在微信开发者工具目录空白处右击 在外部终端窗口中打开或直接在文件路径中输入cmd回车 2 安装之前初始化npm包 再安装 npm init y 通过 npm 安装 npm i vant weapp
  • Ubuntu下如何将普通用户提升到root权限

    1 打开超级终端 输入指令sudo gedit etc passwd 2 则找到crystal x 1000 1000 crystal home linuxidc bin bash 将两个1000改成0即可 3 重新登陆之后打开超级终端发现
  • BLEU 评价指标总结

    Bleu 评测 一 Bleu通常用来度量一组机器产生的翻译句子集合 candidates 与一组人工翻译句子集合 references 的相似程度 Bleu的具体计算过程看下图 在这里解释一下 式中的n 为当前匹配n gram的长度 这里的
  • Win10 + vs2017 编译并配置tesseract4.1.0

    tesseract 是一个开源的OCR Optical Character Recognition 光学字符识别 引擎 本文就介绍一下自己在编译 tesseract4 1 0时遇到的一些坑 希望能给大家带来一些帮助 一 下载 tessera
  • mybatis mysql autoreconnect=true_Mysql8.0主从搭建,shardingsphere+springboot+mybatis读写分离...

    cd usr local mysql mkdir mysql files chown mysql mysql mysql files chmod 750 mysql files bin mysqld initialize user mysq
  • StringUtils中 isNotEmpty 和isNotBlank的区别 以及StringUtil类的方法

    StringUtils方法的操作对象是java lang String类型的对象 是JDK提供的String类型操作方法的补充 并且是null安全的 即如果输入参数String为null则不会抛出NullPointerException 而
  • Apollo:实时通信架构CyberRT入门

    发现一开始就深入源码 很容易陷进去 特别是模块非常多的情况 需要看很多遍才能理解清楚 要写出更容易理解的文档 需要的不是事无巨细的分析代码 更主要的是能够把复杂的东西抽象出来 变为简单的东西 一个很简答的例子是画函数调用流程图很简单 但是要
  • C++指针定义和使用

    目录 1 指针简介 2 指针的声明和使用 1 指针简介 学习指针前需要先分清几个概念 1 1内存单元的地址和内存单元的内容 在程序中定义一个变量 当程序进行编译时就会给定义的变量分配内存单元 这个内存单元的大小由变量的数据类型决定 例如对有
  • 算法

    算法的效率 算法的效率主要由以下两个复杂度来评估 时间复杂度 评估执行程序所需要的时间 可以估算出程序对处理器的使用程度 空间复杂度 评估执行程序所需要的的存储空间 可以估算出程序对计算机内存的使用程度 设计算法时 一般要先考虑系统环境 然