时间复杂度_空间复杂度

时间复杂度_空间复杂度

1.算法效率

算法效率分析分为两种:第一种是时间效率,第二种是空间效率

时间效率被称为时间复杂度,而空间效率被称作空间复杂度。时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间。

在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度

2.时间复杂度

2.1时间复杂度的概念

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

一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。

2.2大O的渐进表示法

// 请计算一下Func1 基本操作执行了多少次?
void Func1(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--){++count;}printf("%d\n", count);
}int main()
{Func1(10);// 130Func1(100);// 10210Func1(1000);// 1002010// 我们发现该函数需要执行 N^2 + 2*N + 10 次// 那么这个函数的时间复杂度应该是  N^2 + 2*N + 10 次 才对// 而我们使用大O阶方法,该函数的时间复杂度为 O(N^2) return 0;
}

实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大0的渐进表示法。

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

推导大O阶方法:

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

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

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

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

最坏情况:任意输入规模的最大运行次数(上界)

平均情况:任意输入规模的期望运行次数

最好情况:任意输入规模的最小运行次数(下界)

例如: 在一个长度为N数组中搜索一个数据X

最好情况:1次找到

最坏情况:N次找到

平均情况:N/2次找到

在实际中,一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)

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

第一个:
// 请计算Func2的时间复杂度
void Func2(int N)
{int count = 0;for (int k = 0; k < 2 * N; ++k) // 这里的2*N的2会被去掉,因为影响不大{++count;}int M = 10;while (M--){++count;}printf("%d\n", count);
}
// 时间复杂度为O(N)
第二个:
// 计算Func3的时间复杂度 
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;}printf("%d\n", count);
}
// 时间复杂度为 O(N+M) 
// // 如果N远大于M  那就是O(N)  反之则是O(M)
第三题:
// 计算Func4的时间复杂度 
void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++k){++count;}printf("%d\n", count);
}
//  时间复杂度为O(1)
第四题:
// 计算strchr的时间复杂度?
const char* strchr(const char* str, int character);
// 时间复杂度为O(N) 因为尽管我们只调用了一次,但是里面的运算 也是算在时间复杂度里面的
第五题:
// 计算Bubblesort的时间复杂度? [冒泡排序]
void BubbleSort(int* a, int n)
{for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], & a[i]);exchange = 1;}}if (exchange == 0)break;}
}
// 准确的时间复杂度是:
// F(N) = 1 + 2 + ... +n-1 的等差数列计算
// 时间复杂度O(N^2)
// 最好的情况O(N)
第六题:
// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{int begin = 0;int end = n;while (begin < end){int mid = begin + ((end - begin) >> 1);if (a[mid] < x)begin = mid + 1;else if (a[mid] > x)end = mid;elsereturn mid;}return -1;
}
// 时间复杂度是O(log2^N)  最好情况是O(1)
// 因为二分查找,每次找都是除于2。 要找到一个数,X是查找的次数  2的X次方 = N 
// 那我们去反向查找这个 X 就是log2 N  【以2为底】
第七题:
// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{if (0 == N)return 1;return Fac(N - 1) * N;
}// 时间复杂度为 O(N)   如果这个有循环调用了N次递归,那么时间复杂度为O(N^2)
第八题:
// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N)
{if (N < 3)return 1;return Fib(N - 1) + Fib(N - 2);
}
// 大体上一共要调用 2^0 + 2^1 + 2^2 + ... +2^(n - 1) = 2^n - 1  次  
// 实际上总有递归先结束调用 比如Fib(N - 2)就会比Fib(N - 1)先结束调用 
// 那么实际上的调用次数并没有 2^n - 1 次 那么多,但是影响微乎其微 
// 所以时间复杂度为 O(2^n) 

如图所示:

image-20240429104156382

实际上这个O(2^N)这个时间复杂度 是非常慢的。

那我们要怎么样去优化这个代码呢?

我们知道 时间复杂度太大的原因是 进行了太多次的重复计算,那我们就让这个重复计算变少不就行了。

我们看下面的代码:

// 优化
long long* Fib_r(size_t N)
{// 我们知道 上面代码之所以时间复杂度如此大 是因为进行了太多次重复的运算// 创建一个数组,把每一次计算的数 都存起来 这样的话我们就可以避免进行重复的运算long long* fibArray = (long long*)malloc(sizeof(long long) * (N + 1));fibArray[0] = 0;if (N == 0) return	NULL; fibArray[1] = 1;// 以空间换时间for (int i = 2; i <= N; i++){fibArray[i] = fibArray[i - 1] + fibArray[i - 2];}return fibArray;
}
// 在这种情况下 我们经过优化的时间复杂度就是 O(N)int main()
{long long* result = Fib_r(1000);printf("%lld\n", result[1000]);  // 817770325994397771return 0;
}

经过优化后 时间复杂度降低 为 O(N)。

实例答案及分析:
  1. 实例1基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N)

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

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

  4. 实例4基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为 O(N)

  5. 实例5基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N^2)

  6. 实例6基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(logN) ps:logN在算法分析中表示是底数为2,对数为N。有些地方会写成lgN。(建议通过折纸查找的方式讲解logN是怎么计算出来的)

  7. 实例7通过计算分析发现基本操作递归了N次,时间复杂度为O(N)。

  8. 实例8通过计算分析发现基本操作递归了2N次,时间复杂度为O(2N)。(建议画图递归栈帧的二叉树讲解)

3.空间复杂度

前面我们说了,时间复杂度不计算时间,计算大概的运算次数。

同样的,空间复杂度不计算空间,计算的大概定义的变量个数。

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度

空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。(因为现代科技下,空间过于庞大,对于GB这个单位来说。占用多少字节不在那么重要了)

空间复杂度计算规则基本跟实践复杂度类似,也使用O渐进表示法

注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。

常见空间复杂度计算例子:

实例1:

// 计算BubbleSort的空间复杂度?
void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0; //  只有这里创建变量 for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}
// 空间复杂度是O(1) O(1)不是1个 而是代表创建的变量是常数个

实例2:

// 计算Fibonacci的空间复杂度?
// 返回斐波那契数列的前n项
long long* Fibonacci(size_t n)
{if (n == 0)return NULL;long long* fibArray = (long long*)malloc((n + 1) * sizeof(long long));fibArray[0] = 0;fibArray[1] = 1;for (int i = 2; i <= n; ++i){fibArray[i] = fibArray[i - 1] + fibArray[i - 2];}return fibArray;
}
// 开辟了n+1的变量   空间复杂度为O(n)  

实例3:

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{if (N == 0)return 1;return Fac(N - 1) * N;
}
// 每次递归都会开辟一个函数栈帧,这个栈帧里面会创建许多变量 (如寄存器等等)
// 每个栈帧里面的创建变量都是常数   n 次递归  
// 空间复杂度为 O(N) 

再来看一个拓展:

// 计算斐波那契递归Fib的空间复杂度?
long long Fib(size_t N)
{if (N < 3)return 1;return Fib(N - 1) + Fib(N - 2);
}

前面我们经过学习和计算,我们知道了该代码的时间复杂度是O(2^N)。

那我们也可以来解析一下空间复杂度是多少。

首先,我们知道,该函数大概会调用 2^N - 1 次, 实际上比这个次数少,但是大体是这个次数,而我们又知道每次调用函数都会创建函数栈帧,在栈帧中我们会创建常数个变量,也就是调用一次函数的空间复杂度是O(1),而我们这里大概会调用2^N - 1 次,因此该函数的空间复杂度就是O(2^N)。

// 每个栈帧里面的创建变量都是常数 n 次递归
// 空间复杂度为 O(N)


再来看一个拓展:```c
// 计算斐波那契递归Fib的空间复杂度?
long long Fib(size_t N)
{if (N < 3)return 1;return Fib(N - 1) + Fib(N - 2);
}

前面我们经过学习和计算,我们知道了该代码的时间复杂度是O(2^N)。

那我们也可以来解析一下空间复杂度是多少。

首先,我们知道,该函数大概会调用 2^N - 1 次, 实际上比这个次数少,但是大体是这个次数,而我们又知道每次调用函数都会创建函数栈帧,在栈帧中我们会创建常数个变量,也就是调用一次函数的空间复杂度是O(1),而我们这里大概会调用2^N - 1 次,因此该函数的空间复杂度就是O(2^N)。

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

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

相关文章

vue什么是服务端渲染(SSR)

Vue服务端渲染&#xff08;SSR&#xff09;在优化SEO方面有着显著的优势&#xff0c;因为它允许搜索引擎直接访问服务器渲染的页面&#xff0c;从而更容易解析页面内容。以下是一些关于如何使用Vue SSR优化SEO的建议&#xff1a; 为每个URL生成静态HTML&#xff1a;Vue SSR允许…

达摩院 2025届暑期实习 大模型算法

文章目录 写在前面一面/技术面 2024/4/7 晚上19:00-20:00二面/技术面 2024/4/23 早上11:15-12:15 写在前面 学校情况&#xff1a;211本中9硕&#xff0c;本硕都是计算机科班&#xff0c;但研究方向并不是NLP&#xff0c;而是图表示学习论文情况&#xff1a;1A(NeurIPS)1B(ICDM…

C#技巧之同步与异步

区别 首先&#xff0c;同步就是程序从上往下顺序执行&#xff0c;要执行完当前流程&#xff0c;才能往下个流程去。 而异步&#xff0c;则是启动当前流程以后&#xff0c;不需要等待流程完成&#xff0c;立刻就去执行下一个流程。 同步示例 创建一个窗体&#xff0c;往窗体里…

面试 Java 基础八股文十问十答第二十八期

面试 Java 基础八股文十问十答第二十八期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01;关注专栏后就能收到持续更新&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;动态代理是什么&am…

2131 - 枚举-练习-涂国旗

2131 - 枚举-练习-涂国旗 c刷题 超能力编程 分析 枚举涂w的底边和涂b的底边即可 剩下的部分都涂r 数据范围这么小,暴力枚举&#xff0c;代码简单难度低。搜索什么的用不着啦&#xff01; 那么问题来了&#xff1a;怎么枚举呢&#xff1f; 我们只要枚举白与蓝、蓝与红的边界&…

【SQL基础】mysql中如何将日期时间类型转换为日期类型

在MySQL中&#xff0c;将DATETIME类型的数据转换为日期格式可以通过使用DATE()函数来实现。DATE()函数可以从DATETIME或TIMESTAMP类型的值中提取出日期部分。 以下是几种将DATETIME转换为日期格式的示例&#xff1a; 直接转换DATETIME列为日期&#xff1a; SELECT DATE(date…

Containerd方式部署K8s集群

1.1 Kubernetes基础环境部署 kubernetes有多种部署方式&#xff0c;目前主流的方式有kubeadm、minikube、二进制包 minikube&#xff1a;一个用于快速搭建单节点kubernetes的工具 kubeadm&#xff1a;一个用于快速搭建kubernetes集群的工具 二进制包 &#xff1a;从官网下载…

【DPU系列之】DPU中的ECPF概念是什么?全称是什么?(E CPF对标H CPF;embedded CPU function ownership)

ECPF&#xff1a;embedded CPU function ownership。 嵌入式CPU运转ownership。也叫DPU模式&#xff0c;是DPU工作运转3种模式之一&#xff0c;也是默认的模式。这里的嵌入式CPU指的是DPU上ARM CPU&#xff0c;表示网卡所有资源和功能被embedded CPU全权管理&#xff0c;行使所…

【动态规划】投资问题

本文利用markdown基于https://blog.csdn.net/qq_41926985/article/details/105627049重写,代码部分为本人编辑 代码要求 应用动态规划方法&#xff0c;求解投资问题&#xff0c;实现下面的例子。 #define MAX_N 4 //最大投资项目数目 #define MAX_M 5 //最大投资钱数(万元) /…

【机器视觉】yolo-world-opencvsharp-.net4.8 C# 窗体应用程序

这段代码是基于 OpenCvSharp, OpenVinoSharp 和 .NET Framework 4.8 的 Windows Forms 应用程序。其主要目的是加载和编译机器学习模型&#xff0c;对输入数据进行推理&#xff0c;并显示结果。 下面是该程序的主要功能和方法的详细总结&#xff1a; 初始化 OpenVINO 运行时核心…

基于Pytorch深度学习——卷积神经网络(卷积层/池化层/多输入多输出通道/填充和步幅/)

本文章来源于对李沐动手深度学习代码以及原理的理解&#xff0c;并且由于李沐老师的代码能力很强&#xff0c;以及视频中讲解代码的部分较少&#xff0c;所以这里将代码进行尽量逐行详细解释 并且由于pytorch的语法有些小伙伴可能并不熟悉&#xff0c;所以我们会采用逐行解释小…

【DPU系列之】如何通过带外口登录到DPU上的ARM服务器?(Bluefield2举例)

文章目录 1. 背景说明2. 详细操作步骤2.1 目标拓扑结构2.2 连接DPU带外口网线&#xff0c;并获取IP地址2.3 ssh登录到DPU 3. 进一步看看系统的一些信息3.1 CPU信息&#xff1a;8核A723.2 内存信息 16GB3.3 查看ibdev设备 3.4 使用小工具pcie2netdev查看信息3.5 查看PCIe设备信息…

python笔记:gensim进行LDA

理论部分&#xff1a;NLP 笔记&#xff1a;Latent Dirichlet Allocation &#xff08;介绍篇&#xff09;-CSDN博客 参考内容&#xff1a;DengYangyong/LDA_gensim: 用gensim训练LDA模型&#xff0c;进行新闻文本主题分析 (github.com) 1 导入库 import jieba,os,re from ge…

安卓手机APP开发__用媒体会话服务进行后台播放

安卓手机APP开发__媒体开发部分__用媒体会话服务进行后台播放 目录 概述 使用一个媒体会话服务 实现服务的生命周期 提供对媒体会话的读取 在配置文件中声明服务 概述 当APP不在前台时&#xff0c;经常希望能够播放媒体。例如&#xff0c;一个音乐播放器 在用户锁屏或者…

【云原生】Docker 的网络通信

Docker 的网络通信 1.Docker 容器网络通信的基本原理1.1 查看 Docker 容器网络1.2 宿主机与 Docker 容器建立网络通信的过程 2.使用命令查看 Docker 的网络配置信息3.Docker 的 4 种网络通信模式3.1 bridge 模式3.2 host 模式3.3 container 模式3.4 none 模式 4.容器间的通信4.…

Stream流操作

看到Stream流这个概念&#xff0c;我们很容易将其于IO流联系在一起&#xff0c;事实上&#xff0c;两者并没有什么关系&#xff0c;IO流是用于处理数据传输的&#xff0c;而Stream流则是用于操作集合的。 当然&#xff0c;为了方便我们区分&#xff0c;我们依旧在这里复习一下…

长期找 AI 专家,邀请参加线上聊天直播

诚邀 AI 专家参加线上聊天&#xff0c;成为嘉宾。 分享前沿观点、探讨科技和生活 除节假日外&#xff0c;每周举办在线聊天直播 根据话题和自愿形式结合&#xff0c;每期 2~3 位嘉宾 成为嘉宾&#xff0c;见下&#xff1a;

小程序端的懂车帝二手车数据采集

import datetime import random import string import time import pymysql import requests import json def mysql_sql(conn, cur): """创建一个存储数据表和一个存储链接表""" cur.execute( CREATE TABLE if not exists dcd_x…

AcWing 851:spfa求最短路 ← 链式前向星存图

【题目来源】https://www.acwing.com/problem/content/853/【题目描述】 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c; 边权可能为负数。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果无法从 1 号点走到 n 号点&#xff0c;则输出 i…

ADS软件(PathWave 先进设计系统软件)分享与安装

ADS软件的简介 ADS软件&#xff08;Advanced Design System&#xff09;主要用于射频&#xff08;RF&#xff09;、微波&#xff08;Microwave&#xff09;和毫米波&#xff08;Millimeter-wave&#xff09;电路的设计、仿真和分析。它提供了一套强大的工具和功能&#xff0c;…