时间复杂度 空间复杂度 ---java

news2024/10/5 23:30:37

目录

一. 算法效率

二.时间复杂度 

2.1 时间复杂度的概念

2.2 大O的渐进表示法

2.3常见时间复杂度计算举例  

三. 空间复杂度


一. 算法效率

算法效率分析分为两种:第一种是时间效率,第二种是空间效率
时间效率被称为时间复杂度,而空间效率被称作 空间复杂度
时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间.  
在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。

二.时间复杂度 

2.1 时间复杂度的概念

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

2.2 大O的渐进表示法

// 请计算一下 func1 基本操作执行了多少次?
void func1 ( int N ){
        int count = 0 ;
        for ( int i = 0 ; i < N ; i ++ ) { //N
                for ( int j = 0 ; j < N ; j ++ ) { //N
                        count ++ ;
                }
        }
        for ( int k = 0 ; k < 2 * N ; k ++ ) { //2*N
                count ++ ;
        }
        int M = 10 ;
        while (( M -- ) > 0 ) { //10
                count ++ ;
        }
        System . out . println ( count );
}

 Func1 执行的基本操作次数 :

使用O的渐进表示法:

1 、用常数 1 取代运行时间中的所有加法常数。
2 、在修改后的运行次数函数中,只保留最高阶项。
3 、如果最高阶项存在且不是 1 ,则去除与这个项目相乘的常数。得到的结果就是大 O 阶。

 使用大O的渐进表示法以后,Func1的时间复杂度为:

通过上面我们会发现大 O 的渐进表示法 去掉了那些对结果影响不大的项 ,简洁明了的表示出了执行次数。

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

最坏情况:任意输入规模的最大运行次数 ( 上界 )
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数 ( 下界 )
在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为 O(N).

2.3常见时间复杂度计算举例  

实例 1
// 计算 func2 的时间复杂度?
void func2 ( int N ) {
        int count = 0 ;
        for ( int k = 0 ; k < 2 * N ; k ++ ) { //2*N
                count ++ ;
        }
        int M = 10 ;
        while (( M -- ) > 0 ) { //10
                count ++ ;
        }
        System . out . println ( count );
}

 基本操作执行了2N+10次,通过推导大O阶方法--->时间复杂度为 O(N)

实例2

// 计算 func3 的时间复杂度?
void func3 ( int N , int M ) {
        int count = 0 ;
        for ( int k = 0 ; k < M ; k ++ ) { //M
                count ++ ;
        }
        for ( int k = 0 ; k < N ; k ++ ) { //N
                count ++ ;
        }
        System . out . println ( count );
}

基本操作执行了M+N次,有两个未知数MN时间复杂度为 O(N+M) 

实例3

// 计算 func4 的时间复杂度?
void func4 ( int N ) {
        int count = 0 ;
        for ( int k = 0 ; k < 100 ; k ++ ) { //100
                count ++ ;
        }
        System . out . println ( count );
}

 基本操作执行了100次,通过推导大O阶方法,时间复杂度为 O(1).

 实例4

// 计算 bubbleSort 的时间复杂度?
void bubbleSort ( int [] array ) {
        for ( int end = array . length ; end > 0 ; end -- ) {
                boolean sorted = true ;
                for ( int i = 1 ; i < end ; i ++ ) {
                        if ( array [ i - 1 ] > array [ i ]) {
                                Swap ( array , i - 1 , i );
                                sorted = false ;
                        }
                }
                if ( sorted == true ) {
                        break ;
                }
        }
}

 错误思想: 因为是两层循环嵌套, 所以时间复杂度为O(N^2)

时间复杂度的计算是要配合逻辑的

正确解法:

第一层循环: 当end = n时, 里层 i 从1到 n-1, 循环了n-1次

第二层循环: 当end = n-1时, 里层i从1到n-2, 循环了n-2次

第三层循环: 当end = n-2时, 里层i从1到n-3,循环了n-3次

...

第n-2层循环: 当end = 3时, 里层i从1到2,循环了2次

第n-1层循环: 当end = 2时, 里层i从1到1,循环了1次

第n层循环: 当end = 1时, 里层循环不进行

所以共循环了(n-1)+(n-2)+(n-3)+...3+2+1=n*(n-1)/2, 通过推导大O阶方法, 时间复杂度为 O(N^2)

实例5 

// 计算 binarySearch 的时间复杂度?
int binarySearch ( int [] array , int value ) {
        int begin = 0 ;
        int end = array . length - 1 ;
        while ( begin <= end ) {
                int mid = begin + (( end - begin ) / 2 );
                if ( array [ mid ] < value )
                        begin = mid + 1 ;
                else if ( array [ mid ] > value )
                        end = mid - 1 ;
                else
                        return mid ;
        }
        return - 1 ;
}

二分查找法的思路: 共N个数

第一次查找: 砍掉一半,剩下N/2个

第二次查找: 再砍掉一半, 剩下N/2^2个 

第三次查找: 再砍掉一半, 剩下N/2^3个 

...

第X次查找: 再砍掉一半, 剩下N/2^X个 

当只剩下一个数时, 就找到了这个数, 所以N/2^X = 1 , 解得X=log(以2为低的)N

(ps: 在算法分析中表示是底数 2,对数为N,有些地方会写成lgN)

所以, 通过推导大O阶方法, 时间复杂度为 O(lgN).

实例 6
// 计算阶乘递归 factorial 的时间复杂度?
long factorial ( int N ) {
        return N < 2 ? N : factorial ( N - 1 ) * N ;
}

一个一般的递归的时间复杂度计算公式(不是所有递归都适用):

 递归的时间复杂度 = 递归的次数 * 每次递归后执行的次数

上述递归:

递归的次数: 当N<2即N=1时, 结束递归, 每次递归-1, 所以congN开始, 共递归了N次

每次递归后执行的次数:每次递归, 只进行了一次三目运算符的判断, 所以执行的次数=1

所以, N*1=N, 通过推导大O阶方法, 时间复杂度为 O(N).

实例 7
// 计算斐波那契递归 fibonacci 的时间复杂度?
int fibonacci ( int N ) {
return N < 2 ? N : fibonacci ( N - 1 ) + fibonacci ( N - 2 );
}

 还是用上述公式:

 递归的时间复杂度 = 递归的次数 * 每次递归后执行的次数

递归的次数:我们画图理解一下

先以F(5)为例:每个F(x)代表一次递归

我们可以看到, 从最上面到最下面, n从5变成了1,也就是一共有五层

第一层有1个数, 第二层有2个数, 第三层有4个数, 我可以得到规律:

第n层有2^(n-1)个数

所以递归的次数为:1+2+4+...+2^(n-1)=2^n +1

每次递归后执行的次数:每次递归, 只进行了一次三目运算符的判断, 所以执行的次数=1

所以, (2^n +1)*1 = 2^n +1, 通过推导大O 阶方法,  时间复杂度为 O(2^N ).

三. 空间复杂度

空间复杂度是对一个算法在运行过程中 临时占用存储空间大小的量度 。空间复杂度不是程序占用了多少 bytes 的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟时间复杂度类似,也使用 O 渐进表示法
实例 1
// 计算 bubbleSort 的空间复杂度?
void bubbleSort ( int [] array ) {
        for ( int end = array . length ; end > 0 ; end -- ) {
                boolean sorted = true ;
                for ( int i = 1 ; i < end ; i ++ ) {
                        if ( array [ i - 1 ] > array [ i ]) {
                                Swap ( array , i - 1 , i );
                                sorted = false ;
                        }
                }
                if ( sorted == true ) {
                        break ;
                }
        }
}
使用了常数个额外空间,所以空间复杂度为 O(1)

实例 2
// 计算 fibonacci 的空间复杂度?
int [] fibonacci ( int n ) {
        long [] fibArray = new long [ n + 1 ];
        fibArray [ 0 ] = 0 ;
        fibArray [ 1 ] = 1 ;
        for ( int i = 2 ; i <= n ; i ++ ) {
                fibArray [ i ] = fibArray [ i - 1 ] + fibArray [ i - 2 ];
        }
        return fibArray ;
}
动态开辟了 N 个空间,空间复杂度为 O(N)

实例3

// 计算阶乘递归 Factorial 的空间复杂度?
long factorial ( int N ) {
        return N < 2 ? N : factorial ( N - 1 ) * N ;
}

递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N) 

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

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

相关文章

post请求参数全大写后台接不到参数

post请求参数全大写后台接不到参数 开发过程中&#xff0c;我们一般都习惯用驼峰命名法&#xff0c;但是特殊情况要求请求参数全大写&#xff08;或者首字母大写&#xff09;&#xff0c;测试验证的时候发现&#xff0c;接收不到请求参数。 前端请求传递&#xff1a; 服务端接…

季报含金量强势推高股价,满帮十年持续拉高数字货运生态天花板

经济活动越发密集&#xff0c;跑在路上的货车和司机们成为最忙碌的角色。11月20日美股盘前&#xff0c;数字货运龙头满帮集团&#xff08;YMM.US&#xff0c;以下简称&#xff1a;满帮&#xff09;发布2023年第三季度财报&#xff0c;其用户规模、业绩数据、履约单量等指标全面…

亚马逊两步验证有哪些验证方法?

亚马逊通常提供多种两步验证的方式&#xff0c;包括短信&#xff08;通过手机接收验证码&#xff09;和认证器应用程序&#xff08;如Google Authenticator、Authy等&#xff09;。选择你偏好的方式。 短信验证&#xff1a; 如果选择短信验证&#xff0c;需要将你的手机号码关联…

《算法通关村——原来这就是堆》

《算法通关村——原来这就是堆》 理解最大堆&#xff1a; 最大堆就是父节点一定比子节点都要大所以就形成了&#xff0c;自然而然根节点就是最大的值了。 如果在最大堆中要插入值得话&#xff0c;那么就需要把值插入到最后&#xff0c;然后一步一步得走上去&#xff0c;也就是…

分布式算法paxos

Paxos算法是什么&#xff1f; Paxos 算法是 基于消息传递 且具有 高效容错特性 的一致性算法&#xff0c;目前公认的解决 分布式一致性问题 最有效的算法之一。 Paxos算法的工作流程&#xff1f; 角色&#xff1a; 在Paxos中有这么几个角色&#xff1a; Proposer&#xff08;提…

Centos7 离线安装 CDH7.1.7

1. 安装CDH的准备工作&#xff08;所有节点都要执行&#xff09; 1.1 准备环境 角色 IP k8s-master 192.168.181.129 k8s-node1 192.168.181.130 k8s-node2 192.168.181.131 1.2 安装JDK # https://www.oracle.com/java/technologies/downloads/#java11 wget rpm -ivh…

知虾官网:探索跨境电商数据的新平台

随着电子商务的快速发展&#xff0c;跨境电商已成为全球贸易的重要组成部分。为了帮助企业更好地了解市场、选品、分析竞争对手和科学运营&#xff0c;知虾官网应运而生。本文将为您介绍知虾官网的功能和优势&#xff0c;以及广州萌啦信息科技有限公司的背景。 一、知虾官网的功…

电源控制系统架构(PCSA)之系统分区电源域

目录 4.2 电源域 4.2.1 电源模式 4.2.2 电源域的选择 4.2.3 系统逻辑 4.2.4 Always-On域 4.2.5 处理器Clusters 4.2.6 CoreSight逻辑 4.2.7 图像处理器 4.2.8 显示处理器 4.2.9 其他功能 4.2.10 电源域层次结构要求 4.2.11 SOC域示例 4.2 电源域 电源域在这里被定…

VScode安装使用DevChat插件

前言 DevChat是一个开源平台,使开发人员能够更有效地将人工智能集成到代码生成和文档中。DevChat的目标是超越简单的代码自动完成和对代码片段的有限操作。DevChat为开发人员提供了一种非常实用和有效的方式来与大型语言模型(llms)进行交互和协作。 一、安装DevChat插件 De…

西米支付:简单介绍一下支付公司的分账功能体系

随着互联网的普及和电子商务的快速发展&#xff0c;支付已经成为人们日常生活的重要组成部分。支付公司作为第三方支付平台&#xff0c;为消费者和商家提供了便捷、安全的支付方式。而在支付领域中&#xff0c;分账功能是一个非常重要的功能&#xff0c;它可以帮助企业实现资金…

一篇总结 Linux 系统启动的几个汇编指令

学习 Linux 系统启动流程&#xff0c;必须熟悉几个汇编指令&#xff0c;总结给大家。 这里不是最全的&#xff0c;只列出一些最常用的汇编指令。 一&#xff0e;数据处理指令 1.数据传送指令 【MOV指令】 把一个寄存器的值(立即数)赋给另一个寄存器&#xff0c;或者将一个…

如何巧用拨测监控中的自定义维度?

在拨测监控的场景中&#xff0c;用户往往很关心拨测的资源对象是否正常。在当前拨测采集策略中&#xff0c;拨测采集对象本身可能无法反映出配置的拨测地址&#xff0c;用户可能还需要找到具体的拨测采集策略后才能确认拨测地址。 本期EasyOps产品使用最佳实践&#xff0c;我们…

『Confetti 喜庆散花插件の使用』

以下用 VUE3 语法 举例使用&#xff1a; npm install js-confetti<script setup lang"ts"> import JSConfetti from js-confetticonst confetti new JSConfetti()function showConfetti() {confetti.addConfetti() } </script><template><h1 …

井盖位移传感器怎么监测井盖安全

井盖在城市基础设施建设中扮演着不可或缺的角色&#xff0c;虽然看似并不起眼但确实是城市规划中一个重要的组成部分。在城市规划建设之初都需要首先考虑排水系统的设计&#xff0c;而井盖作为排水系统的一个重要组成部分&#xff0c;一旦出现问题便会造成交通中断或者环境受影…

Unity SRP 管线【第三讲:URP 光照】

3.2.3 以前属于Shader部分&#xff0c;Shader部分不进行讲解。 这里只涉及Unity内部管线的设置问题。 文章目录 3.2.3 向GPU发送灯光数据设置光源数据设置主光源设置额外点光源 Shader中的数据 3.2.3 向GPU发送灯光数据 在UniversalRenderPipeline.cs > RenderSingleCamera…

广告屏LED屏断电检测远程控制开关方案应用钡铼技术S270

广告屏LED屏断电检测&#xff1a; 广告屏和LED屏在商业和公共场所的广泛应用中扮演着重要角色&#xff0c;但由于断电问题可能导致广告屏无法正常显示&#xff0c;进而影响广告宣传效果和客户体验。而S270作为一种高效稳定的远程控制开关&#xff0c;可以实现广告屏LED屏的断电…

Linux【安全 01】云服务器主机安全加固(修改SSHD端口、禁用登陆失败的IP地址、使用密钥登录)

云服务器主机安全加固 1.SSH登录尝试的系统日志信息2.安全加固方法2.1 修改SSHD端口2.2 禁用登陆失败的IP地址2.3 使用密钥登录 3.总结 1.SSH登录尝试的系统日志信息 Last failed login: Sat Oct 7 14:10:39 CST 2023 from xxx.xx.xx.xxx on ssh:notty There were 10 failed …

虾皮知虾数据分析软件:优化您的电商经营

在当今竞争激烈的电商市场&#xff0c;了解市场动态、分析竞争对手和产品趋势是成功经营的关键。虾皮知虾数据分析软件是一款强大的工具&#xff0c;为电商卖家提供了全面的数据采集和分析功能。本文将介绍虾皮知虾数据分析软件的特点和功能&#xff0c;以及如何利用它来优化您…

Linux基本命令操作 —— 文件夹/文件的创建,删除,查看,重命名......(简单理解 快速上手)

目录 1. 基础命令 1.1 显示当前目录&#xff1a;pwd 1.2 改变当前目录&#xff1a;cd 2. 文件夹的操作命令 2.1 创建文件夹&#xff1a;mkdir 2.2 查看文件夹&#xff1a;ls 2.3 删除文件夹&#xff1a;rmdir &#xff08;不推荐&#xff09; 2.4 复制文件夹&#xff1…

竞赛选题 题目:基于LSTM的预测算法 - 股票预测 天气预测 房价预测

文章目录 0 简介1 基于 Keras 用 LSTM 网络做时间序列预测2 长短记忆网络3 LSTM 网络结构和原理3.1 LSTM核心思想3.2 遗忘门3.3 输入门3.4 输出门 4 基于LSTM的天气预测4.1 数据集4.2 预测示例 5 基于LSTM的股票价格预测5.1 数据集5.2 实现代码 6 lstm 预测航空旅客数目数据集预…