数据结构之时间复杂度&&空间复杂度的计算

2023-09-21 15:33:30

数据结构:计算机如何存储数据的问题。DS关心的是如何高效的进行数据的读写。

算法:在特定的数据集上(不关心怎么进行具体数据的读写),如何利用数据完成特定的功能。算法本质上就是一系列运算的先后集合。

那么,如何评价一个算法的好坏?

从两种维度进行:时间效率与空间效率

时间复杂度:主要衡量一个算法的运行速度(不是实际计算机的运行速度,是理论值,在所有计算机上通用的描述) 

空间复杂度:主要衡量一个算法正常执行所需要的额外空间大小,不是当前数据集本身占用的空间

所谓额外空间:为了解决这个问题,新开辟的内存大小

1. 时间复杂度

不是具体的时间,在计算机领域,其实是一个数学函数。

因为不同的计算机硬件不同,相同的算法在不同的CPU,内存这些硬件上具体的执行时间都会有差异,而且差异还挺大!

那么,利用数学函数,描述一个算法的基本操作的执行次数,就是所谓的时间复杂度

 在函数中,基本的执行语句可以不统计,基本语句和变量N毫无关系,时间复杂度看重的是执行次数和传入的变量之间的关系

使用大O渐进表示法(数学函数,表示渐进函数)来描述一个算法的时间效率。

1.1 推导大O阶的方法:

(1)用常数1来代替函数中的所有加法常数(只要是常量,常数,都会1,无论常量有多大,用1代替)

因为常数和传入数值N变量毫无关系,此算法是一个稳定的时间效率算法,和变量无关!

(2)只保留函数的最高阶项,所有的低阶省略不计

(3)若最高阶项存在且项数不为1,统一将其最高阶的项数看做1

先看最高阶在哪,低阶全部省略;若没有变量参与,统一都是常数1。

例如:F(N)=N^2+2N+10                  时间复杂度:F(N)=O(N^2)

1.2 时间复杂度评价标准

一个算法的时间复杂度存在最好、最坏、平均时间复杂度。

  • 最好:任意输入的数据,最小的执行次数(算法的下界)
  • 最坏:任意输入的数据,最大的执行次数(算法的上界)
  • 平均:任意输入的数据,平均的执行次数

eg:在长度为N的数组中,搜索一个数据x

最坏:若x刚好在数组的末尾或者不存在该数据,则需将整个数组进行遍历,执行N次才有结果

最好:若x刚好在数组首端,执行一次就能得到结果

平均:1+2+3+......+N=(1+N)*N/2(N个数据的总共查找次数)

           单个数据((1+N)*N/2)/N =N/2       时间复杂度:O(N)

衡量一个算法的时间复杂度取上界,即考虑算法的最坏情况,使用大O阶时间复杂度来描述最坏情况就是整个算法的时间复杂度。

1.3 相关例题

1. 大O阶看的是变量的最高阶,若存在多个输入变量,都需要保留各个变量的最高阶。

此时N和M都是变量,无法得知N和M谁大谁小,推导大O阶时,N和M都保留其最高阶。

时间复杂度:O(N+M)

2. N和循环次数没有关系,循环次始终为100。O(1) 和变量N无关

时间复杂度:O(1)

 3.  冒泡排序

循环次数为N^2,内外层都执行N次。时间复杂度:O(N^2)

 4. 二分查找

该循环每次不是从0--N,而是每次从区间的中间位置将区间一分为二,mid=N/2;

n/2+n/4+n/8+.....+n/16+....,要求这个执行次数,以2为底N的对数 

时间复杂度:O(logn)

 public static int binSearch(int[] arr,int value){
        int left=0;
        int right=arr.length-1;
        while(left<=right){
            int mid=(right+left)/2;
            if(value<arr[mid]){
                right=mid-1;
            }else if(value>arr[mid])
            {
                left=mid+1;
            }else{
                return mid;
            }
        }
        //抛出运行时异常,代表未找到元素(不能返回-1,因为-1也有可能为元素本身)
        throw new NoSuchElementException("没有找到该元素!");
    }

(1)原则:忽略底数(换底公式:无论底数为多少,都可以换成以10为底的)

O(logn)==>忽略底数,即:对数级别的时间复杂度

如果一个算法能够做到对数级别的时间复杂度,则是非常优秀的!

(2)如何分析一个算法是对数级别的时间复杂度?

若算法是不断的将一个变量除以一个常数:N/2,或者N/3,一直除到为1或者0,则这个算法一定是对数级别的算法O(logn),也叫折纸算法

(3)分析O(N)、O(logN)、O(N^2)随着N的不断变化,三个不同函数的变化趋势。

 二分查找在一亿的有序集合中,最坏只需要29次就能查到特定元素。

5. 关于递归算法的执行时间分析

public static int fibo(int N){
        return N<=2? N:fibo(N-2)+fibo(N-1);
    }

执行次数:将fibo函数展开,执行次数就是下图中的方框个数,也就是说,求执行次数就是求结点的总个数。 

2+4+6+8+....+..... 求二叉树的节点个数2^N+1==> 时间复杂度:O(2^N)

 public static int factor(int N){
        //1.base case
        if(N<=2){
            return N;
        }
        return N*factor(N-1);
    }

 执行次数:上述代码展开类比为:for(int i=1;i<N;i++){ int n*=i;},因此,时间复杂度为O(N)

总结:看一个递归算法的时间复杂度,就看递归展开,函数的执行次数即可!

2. 空间复杂度 

 算法执行过程中,额外开辟的空间大小。

时间复杂度描述的是算法的执行次数;

空间复杂度描述的是一个算法额外开辟的临时变量个数,也是使用大O渐进表示法。

eg:冒泡排序的空间复杂度O(1),额外开辟了一个临时变量temp。

2.1 分析空间复杂度

1. 斐波那契函数的空间复杂度

 public static int[] fibo(int N){
        int[] ret=new int[N+1];
        ret[0]=1;
        ret[1]=1;
        for (int i = 2; i<=N ; i++) {
            ret[i]=ret[i-1]+ret[i-2];
        }
        return ret;
    }

 由于函数中出现了new int[N+1];也就是说额外开辟了一个新数组,因此空间复杂度为O(N)。

2. 递归函数的空间复杂度

对于递归函数来说,每执行一次函数,就会在系统栈上产生新的栈帧,这就属于临时空间。

 public static int factor(int N){
        return N<=2?N:N*factor(N-1);
    }

也就是说观察其展开的次数。

空间复杂度:O(N)

总结:

非递归:空间复杂度就是看函数内部new了什么

递归:看函数的展开次数,每次调用一次函数,都会额外在栈上开辟栈帧空间。

一般空间复杂度最常见的为O(1)或O(N)

在题目中,出现空间复杂度要求为O(1),代表此时不能开辟新的数组且不能使用递归。

更多推荐

量化分析革新金融服务软件的三种方式

金融服务软件行业爱死量化分析了。为什么呢?因为在这个本质上不可预测的行业中,量化分析提供了一种确定性,或者至少是类似于确定性的东西。市场总是在变动,利润也起伏不定。交易达成了,然后落空,又再次达成,从交易大厅到董事会,纳秒级的差异可能成就巨大成功或带来重大损失。如果没有量化分析,我们难以预测这些事情会在何时、何地、以何

word快捷键、conda一些安装问题、坐标转换初阶

整理一下今天所学吧!用博客来记录还是比较理想的,因为可以时常观看。首先是word:上下标:ctrl+=,ctrl+shift+=斜体:ctrl+ialt+=插入公式!!!选中:shift+方向矩阵公式可以直接选中进行添加和修改。接着是一些环境:anaconda中,在c盘不好用时,去d盘打开首先需要给它初始化了:cond

etcd之读性能主要影响因素

1、Raft模块-线性读ReadIndex-节点之间的RTT延时、磁盘IO线性读时Follower节点首先会向Raft模块发送ReadIndex请求,此时Raft模块会先向各节点发送心跳确认,一半以上节点确认Leader身份后由leader节点将已提交日志索引(committedindex)封装成ReadState结构

初识Java 10-1 集合

目录泛型和类型安全的集合基本概念添加一组元素打印集合ListIterator(迭代器)本笔记参考自:《OnJava中文版》在进行程序设计时我们会发现,程序总是会根据某些在运行时才能知道的条件来创建新的对象。这意味着,我们必须能够随时随地创建任意数量的对象。Java提供了几种方法来持有对象(的引用),其中之一就是数组。数

华为云云耀云服务器L实例评测|华为云上安装监控服务Prometheus三件套安装

文章目录华为云云耀云服务器L实例评测|华为云上试用监控服务Prometheus一、监控服务Prometheus三件套介绍二、华为云主机准备三、Prometheus安装四、Grafana安装五、alertmanager安装六、三个服务的启停管理1.Prometheus、Alertmanager和Grafana启动顺序2.

华为云HECS云服务器docker环境下安装redis

当前有个华为云HECS云服务器,已经安装了docker环境,准备下docker环境下安装redis。一、HECS云服务器安装docker登录华为HECS云服务器,安装docker环境。安装docker参考如下文章:华为云HECS安装docker并安装mysql-CSDN博客二、拉取redis镜像1、查询redis镜像d

Mybatis SQL构建器

上一篇我们介绍了在Mybatis映射器中使用@SelectProvider、@InsertProvider、@UpdateProvider、@DeleteProvider进行对数据的增删改查操作;本篇我们介绍如何使用SQL构建器在Provider中优雅的构建SQL语句。如果您对在Mybatis映射器中使用@Select

JVM——10.对象的内存布局

这篇文章,我们来了解一下对象在内存中的布局是什么样的。解释:前面有一篇文章我们讲了JVM中类的结构,那里讲的是一个java类,被编译成二进制字节码后,它的结构是什么样的,或者说按照jvm的标准,一个.class文件中类的结构是什么样的。而这里我们要讲的是根据类在堆中创建出来的对象,它在堆中是如何布局,即它在堆中的结构是

计算机操作系统 (王道考研)笔记(二)

重点知识点1内存1.1内存的基础知识1.1.1内存定义、作用1.1.2指令的工作原理1.1.3三种装入策略1.1.4从写程序到程序运行1.1.5链接的三种方式1.1.6总结1.2内存管理1.2.1内存空间的分配与回收a)连续分配管理b)非连续分配管理1)基本分页存储管理2)基本分段存储管理3)段页式存储管理1.2.2内

[LLM+AIGC] 01.应用篇之中文ChatGPT初探及利用ChatGPT润色论文对比浅析(文心一言 | 讯飞星火)

近年来,人工智能技术火热发展,尤其是OpenAI在2022年11月30日发布ChatGPT聊天机器人程序,其使用了Transformer神经网络架构(GPT-3.5),能够基于在预训练阶段所见的模式、统计规律和知识来生成回答,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流以及完成复杂的NLP任务。基于此,为更好

Flutter粒子生成演示

演示:直接上代码:import'dart:math';import'dart:ui';import'package:flutter/material.dart';import'package:kq_flutter_widgets/widgets/chart/ex/extension.dart';classParticl

热文推荐