【数据结构初阶(Java)】认识时间复杂度和空间复杂度

news2024/9/20 16:47:16

目录

 前言:

1、算法效率

2、时间复杂度

1、大O的渐近表示法(不是一个准确的)

 2、时间复杂度练习题(没有明确要求,计算的时间复杂度就是最坏情况下)

3、空间复杂度


 前言:

如何衡量一个算法的好坏呢?这里就要提到时间复杂度空间复杂度的概念。

1、算法效率

算法效率分为两种

第一种是时间效率:时间效率别成为时间复杂度。时间复杂度主要衡量一个算法的运行速度。

第二种是空间效率:空间效率被称为空间复杂度。空间复杂度主要衡量一个算法所需要的额外空间。

2、时间复杂度

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

1、大O的渐近表示法(不是一个准确的)

大O符号(Big O notation): 是用于描述函数渐进行为的数学符号。

推导大O阶方法

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

 算法的时间复杂度还存在最好、平均和最坏的情况:在计算一个算法的时间复杂度的时候,没有明确说明的时候,我们计算的时间复杂度,都是最坏情况下的时间复杂度

  • 最坏情况:任意输入规模的最大运行次数(上界)
  • 平均情况:任意输入规模的期望运行次数
  • 最好情况:任意输入规模的最小运行次数(下界) 

举例: 

 在一个长度为N的数组中查找一个数据x

最好情况:1次找到

最坏情况:N次找到

平均情况:N/2次找到

在实际中一般情况关注的是算法的最坏运行情况。 

来看一个例子:请计算一下func方法基本操作执行了多上次?

public class Test {
    void func(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--)>0){
            count++;
        }
    }
}

我们在计算一个代码的时间复杂度的时候,会找这个代码当中执行次数最多的部分

分析:

上述代码中执行次数最多的是,三个循环中的代码

  • 第一个for循环,由于是for循环中嵌套for循环,所以执行次数为N+N+N+...+N,化简一下为N^{2}
  • 第二个for循环,执行次数为2*N次。
  • 第三个while循环,执行次数为10次。

所以上述代码中的比较精确时间复杂度为 F(N) = N^2+2*N+10

用大O渐近表示法表示上述代码的时间复杂度为O( N^{2})

 2、时间复杂度练习题(没有明确要求,计算的时间复杂度就是最坏情况下)

代码一:

void func2(int N){
        int count = 0;
        for(int k = 0;k <2*N;k++){
            count++;
        }
        int M = 10;
        while((M--)>0){
            count++;
        }
        System.out.println(count);
    }

👀分析:

比较精确的时间复杂度为:F(N) = 2*N + 10;

大O渐近表示法来表示时间复杂度:O(N)

代码二: 

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++;
        }
        System.out.println(count);
    }

👀分析:

第一个for循环中的是M,第二个for循环中的是N,M和N并没有任何关系。

所以用大O渐近表示法来表示时间复杂度为:O(M+N)

代码三:

  void func4(int N){
        int count = 0;
        for(int K = 0;K < 100; K++ ){
            count++;
        }
        System.out.println(count);
    }

 👀分析:

比较精确的时间复杂度为:F(N) = 100;

用大O渐近表示法:因为大O渐近表示法的规则中表示,运行中所有加法常数用常数1取代

所以用大O渐近表示法,时间复杂度为:O(1).

 代码四:

 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;
            }
        }
    }

👀分析:

这里来说明一下N在时间复杂度中代表的是问题的规模,他是一个范围,并不是说这个算法变量为N,时间复杂度就一定用N表示。

  • 这个算法是循环嵌套,第一个for循环循环n次,第二个for循环n-1次,第一个for循环每进一次,第二个for循环就要循环n-1次,所以它比较精确的时间复杂度为 F(N) = N(N-1)
  • 用大O渐近表示法,表示时间复杂度为:O(N^{2})(最坏的情况下)
  • 这个代码的时间复杂度在最好的情况下为:要遍历的数组是有序的,只需要进入第一个for循环一次,就可以得到想要的元素,所以最好情况下为O(N)

 代码五:

public static void bubbleSort(int[] array){
        for(int i = 0;i < array.length-1;i++){
            boolean flg = false;
            for(int j = 0;j < array.length-1-i;j++){
                if(array[j] >array[j+1]){
                    int tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                    flg = true;
                }
            }
            if(flg == false){
                return;
            }
        }
    }

👀分析:

上述代码为冒泡排序:冒泡排序的思想是,相邻两个元素进行比较,交换位置

第一个for循环执行N-1次,表示的是趟数;

第二个for循环执行次数为:N-1+N-2+.....+1,N-1,N-2表示的是每趟比较的次数。这里是一个等差数列,使用等差数列的求和公式:Sn = (首相+尾项)*项数/2 ,用大O渐近表示法,则为O(N^{2})

代码五:

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;
    }

👀分析:

本题是二分查找的代码:计算此题的时间复杂度,不能只看代码,要结合二分查找的思想,来计算时间复杂度。

上图用公式表示为

 计算过程为:\frac{N}{2^{x}}=1

化简:2^{x}=N

得:x=log{_{2}}^{}N

所以用大O渐近表示法,时间复杂度为:O(log{_{2}}^{}N).

 代码六:计算递归函数的时间复杂度(单路递归)

 long factorial(int N){
        return N < 2?N:factorial(N-1)*N;
    }

👀分析:

递归的时间复杂度(递归的次数) * (每次递归后,代码的执行次数)

上述代码中factorial方法中的代码只有一个三目运算符,所以这里的每次递归后代码的执行次数为1

所以该函数的时间复杂度为:F(N) = N*1.

用大O渐近表示法为: O(N).

代码七: 计算斐波那契递归的时间复杂度(多路递归)

  int fibonacci(int N){
        return N < 2?N:fibonacci(N-1)+fibonacci(N-2);
    }

 👀分析:

 

 第一行为1,第二行为2,第三行为4,按照规律,将每行的个数相加,得到的是一个等比数列,根据等比数列求和公式

1+2+4+...+2^(n-1)从2^0开始到2^(n-1),共n项。

通过计算可得:2^n-1

所以该算法的时间复杂度用大O渐近法表示为:O(2^{N})

 


3、空间复杂度

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的度量一个算法在计算机 存储器 上所占用的存储空间,包括存储算法本身所占用的存储空间算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。空间复杂度计算的规则基本和时间复杂度类似,也使用大O渐近表示法

代码一:

public static void bubbleSort(int[] array){
        for(int i = 0;i < array.length-1;i++){
            boolean flg = false;//标志位,用来判断两个相邻元素是否还能进行比较,位置交换
            for(int j = 0;j < array.length-1-i;j++){
                if(array[j] >array[j+1]){
                    int tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                    flg = true;
                }
            }
            if(flg == false){
                return;
            }
        }
    }

 👀分析:

这个算法的问题规模:对N个数据进行排序。

  • 看在这个算法运行过程中有没有临时占用内存的空间,有的同学就会说形参int[ ] array是临时开辟的空间,这个不算,他是冒泡排序所必须的。
  • 这里定义的flg变量是临时的,他和算法的规模没有关系,不管规模多大,他只有一个,他的作用就是作为标志位
  • 上述代码使用了常数个额外空间,所以空间复杂度为O(1)

代码二:

long[] 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;
    }

  👀分析:

计算Fibonacci(斐波那契),当你有n个数据,那么在计算之前先要创建空间,用来存储计算后的结果,随着问题的规模(n)越大,那么创建的数组也就越来越大。该算法开辟了N个空间,所以空间复杂度为O(N).

代码三:计算阶乘递归factorial的空间复杂度

   long factorial(int N){
        return N < 2?N:factorial(N-1)*N;
    }

   👀分析:

递归,每次都要在栈帧上开辟内存,当在递的过程中,开辟空间,当在归的时候,释放空间,这就相当于开辟的临时空间。当我们以N为问题的规模的时候,那么该算法的空间复杂度为O(N).

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

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

相关文章

Java中多线程wait和notify的用法

目录 一、wait和notify/notifyAll的由来 二、wait()方法 三、notify方法 3.1 notify的作用 3.2 wait和notify的 相互转换代码图 3.3 notifyAll 四、为什么需要notify和wait都需要上锁&#xff1f; 五、wait和sleep的对比 前言&#xff1a;由于线程之间是抢占式执行的&a…

Linux常用命令——tftp命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) tftp 在本机和tftp服务器之间使用TFTP协议传输文件 补充说明 tftp命令用在本机和tftp服务器之间使用TFTP协议传输文件。 TFTP是用来下载远程文件的最简单网络协议&#xff0c;它其于UDP协议而实现。嵌入式linu…

RTMP协议封装H264和H265协议详解

RTMP协议封装H264和H265协议详解 文章目录RTMP协议封装H264和H265协议详解1 RTMP和FLV2 RTMP协议封装H264视频流2.1 RTMP发送AVC sequence header2.2 RTMP发送AVCC视频帧数据‘3 RTMP协议封装H265视频流1 RTMP和FLV 有关RTMP和FLV格式详细介绍可查看如下文章&#xff1a; http…

2022 Moonbeam的点点滴滴离不开社区支持

Moonbeam成为首个上线波卡的平行链已经有一周年&#x1f382;啦&#xff0c;这是一段疯狂的旅程&#x1f3cd;。 为了纪念这一时刻&#xff0c;我们通过公开数据来回顾这一年的众多里程碑、更新和整体发生的一切。 让我们来回顾一下Moonbeam在2022年取得了哪些成就吧。 &…

GIS二维电子地图开发总结

二维平面地图&#xff0c;目前支撑设备渲染&#xff0c;真实场景&#xff0c;后期电子围栏&#xff0c;运动轨迹等业务需求做铺垫 一、所涉及的技术栈&#xff1a; 1.Openlayers,加载渲染地图 2.Geoserver 发布wms和wfs&#xff08;&#xff09;服务 3.Arcgis,Arcmap,进行源文件…

3.1、Ubuntu20桌面版远程连接SSHMobaXterm远程连接编辑器

连接SSH 安装系统完成并登陆后&#xff0c;输入 修改源码地址 进入apt文件夹 cd /etc/apt 备份文件 cp sources.list sources.list.bak 修改源码地址 vi sources.list # See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to # newer versions of…

数据结构初级<排序>

本文已收录至《数据结构(C/C语言)》专栏&#xff01; 作者&#xff1a;ARMCSKGT 你的阅读和理解将是我极大的动力&#xff01; 目录 前言 排序的概念 常见排序简述 正文 直接插入排序 原理 代码实现 分析 希尔排序 原理 代码实现 分析 直接选择排序 原理 代码…

类加载的时机与过程

------ 摘自 周志明 《深入理解Java虚拟机》类加载的时机一个类型从被加载到虚拟机内存中开始&#xff0c;到卸载出内存为止&#xff0c;它的整个生命周期将会经历加载&#xff08;Loading&#xff09;、验证&#xff08;Verification&#xff09;、准备&#xff08;Preparati…

6、数组的常见运算

目录 一、数组的算术运算 二、数组的关系运算 三、数组的逻辑运算 一、数组的算术运算 &#xff08;1&#xff09;数组的加减运算&#xff1a;通过格式AB或A-B可实现数组的加减运算。但是运算规则要求数组A和B的维数相同。 示例1&#xff1a; A[1 2 3 4]B[2 4 6 8]C[1 1 …

三种简洁易行的方法解决基于Vue.js的组件通信

在总结Vue组件化编程的数据通信方面&#xff0c;看了网上的很多资料&#xff0c;都是讲父子组件的数据交互也就是参数传递&#xff0c;在组件的通信方面分几种情况&#xff0c;比如父子组件、非父子的兄弟组件、非父子的其他组件等等&#xff0c;这样看来&#xff0c;基于Vue.j…

STC15系列单片机EEPROM读写示例

STC15系列单片机EEPROM读写示例&#x1f33c;STC15手册有关EEPROM描述 &#x1f33e;STC15系列单片机内部集成了大容量的EEPROM&#xff0c;其与程序空间是分开的。利用ISP/IAP技术可将内部DataFlash当EEPROM&#xff0c;擦写次数在10万次以上。EEPROM可分为若干个扇区&#xf…

Android 蓝牙开发——蓝牙协议配置(七)

蓝牙主要分为两种模式&#xff0c;一种是媒体输出&#xff08;Source&#xff09;端&#xff0c;一种是媒体输入&#xff08;Sink&#xff09;端。也可以理解为服务端&#xff08;Server&#xff09;与客户端&#xff08;Client&#xff09;的关系。 蓝牙配置文件&#xff08;B…

4-1指令系统-指令格式

文章目录一.指令的基本格式1.结构2.长度3.根据操作数地址码数目分类&#xff08;1&#xff09;零地址指令&#xff08;2&#xff09;一地址指令&#xff08;3&#xff09;二地址指令&#xff08;4&#xff09;三地址指令&#xff08;5&#xff09;四地址指令二.扩展操作码指令格…

Maven学习(二):Maven基础概念

Maven基础概念一、仓库二、坐标三、全局setting与用户setting区别一、仓库 仓库&#xff1a;用于存储资源&#xff0c;包含各种jar包&#xff1b;仓库分类&#xff1a; 本地仓库&#xff1a;自己电脑上的存储仓库&#xff0c;连接远程仓库获取资源&#xff1b;远程仓库&#x…

信息论复习—离散信道及其容量

目录 信道的简介&#xff1a; 信道的分类&#xff1a; 离散无记忆信道&#xff08;DMC&#xff09;模型&#xff1a; 转移概率&#xff1a; 离散无记忆信道的转移矩阵 输出仅与当前的输入有关&#xff1a; 后验概率&#xff1a; 离散无记忆信道的后验概率矩阵 &#xf…

spring-boot-starter-jdbc和mysql-connector-j依赖爆红的解决办法

spring-boot-starter-jdbc和mysql-connector-j依赖爆红的解决办法 目录spring-boot-starter-jdbc和mysql-connector-j依赖爆红的解决办法出现问题之前出现的问题&#xff1a;解决办法&#xff1a;方案一&#xff1a;第一种是继承 spring-boot-starter-parent 然后 依赖覆盖方案…

怎么用Python测网速?

“speedtest-cli” 是一个 Python 的第三方库&#xff0c;它可以用来在命令行中测试网络速度。它使用了 Speedtest.net 的服务器来进行测速&#xff0c;并可以提供下载和上传速度、延迟、丢包率等信息。使用这个库可以很方便地在终端中测试网络速度&#xff0c;而无需在浏览器中…

轻量级代码生成器加测试数据生成器

轻量级代码生成器加测试数据生成器介绍代码生成常用注解基本使用全局控制属性模板文件相关属性模板文件配置模拟数据生成自定义词库索引注意事项从已经存在的表完成映射,生成模板代码使用步骤Gitee项目链接 介绍 本项目是一个轻量级代码生成器,并提供多种方式来完成模拟数据的…

力扣(LeetCode)2299. 强密码检验器 II(C++/Python3)

题目描述 模拟 仅当密码包含强密码的所有特性&#xff0c;它是一个 强 密码。提示我们&#xff0c;遍历密码&#xff0c;维护 444 个标志&#xff0c;标志记录特性。遍历结束&#xff0c;根据标志判断特性。 class Solution { public:bool strongPasswordCheckerII(string pa…

MySQL建立数据库时字符集和排序规则的选择

文章目录前言一、字符集&#xff1f;二、Mysql中的字符集1.Unicode与UTF8、UTF8MB4、UTF16、UTF32关系2.Mysql新建数据库时选择哪种字符集呢&#xff1f;三、Mysql排序规则四、Mysql查询字符集和排序规则常用的命令前言 在MySQL中&#xff0c;字符集和排序规则是区分开来的&am…