数据结构---时间复杂度+空间复杂度

算法(algorithm)简单说就是解决问题的方法。方法有好坏,同样算法也是,有效率高的算法,也有效率低的算法。衡量算法的好坏一般从时间和空间两个维度衡量,也就是本文要介绍的时间复杂度和空间复杂度。有些时候,时间与空间不可兼得,会出现以时间换空间或者以空间换时间

1.时间复杂度

I.时间复杂度概念

在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?最简单的方法就是将算法运行一遍,但是运行时间容易受到环境、数据规模等的影响,所以才有了这个时间复杂度这个分析方式,一个算法所花费的时间与其中语句的执行次数成正比列,算法中的基本操作的执行次数,为算法的的时间复杂度。

找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。

//请计算Fun1中++count语句总共执行了多少次
void Func1(int N)
{int count = 0;for (int i = 0; i < N; ++i){++count;}for (int k = 0; k< 2*N; ++k){++count;}int M = 0;while (M--){++count;}printf("%d\n",count);
}

 Func1执行的基本操作次数:

  • N=10         F(N)=130
  • N=100       F(N)=10210
  • N=1000     F(N)=1002010

实际中计算时间复杂度时,其实并不一定要计算精确的执行次数,而只需要大概执行次数,因此在计算时使用大O渐近表示法。 

II.大O渐近表示法

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

推导大O阶方法:

(1).用常数1取代运行时间中的所有加法常数

(2).在修改后的运行次数函数中,只保留最高阶项

(3).如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶

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

  • N=10         F(N)=100
  • N=100       F(N)=10000
  • N=1000     F(N)=1000000

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

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

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

比如说:在一个长度为N的数组中搜索一个数据x

最坏情况:N次找到

平均情况:N/2次找到

最好情况:1次找到

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

时间复杂度的计算是确定它的量级,通过确定不同的量级来比较算法的好坏,常见量级如下

完整的如下:

III.示例:

//计算Func2的时间复杂度
void Func2(int N)
{int count = 0;for (int k = 0; k < 2 * N; ++k){++count;}int M = 10;while (M--){++count;}printf("%d\n", count);
}

Func2中第一个循环循环2*N次,第二个while循环循环10次,用数学表达式写的结果就是F(N)=2*N+10 ,经过推导可得,2是系数和常数M对整体的影响较小,故最终的时间复杂度就是O(N)

void Func3(int N, int M)
{int count = 0;for (int k = 0; k < M; M++){++count;}for (int k = 0; k < N; ++k){++count;}printf("%d\n", count);
}

 Func3中第一个循环循环M次,第二个循环循环M次,F(N)=M+N,对应的时间复杂度就是O(M+N)(Omax(M,N)),如果M远大于N,就是O(M),如果N远大于M,就是O(N)

void Func4(int N)
{int count = 0;for (int k = 0; k < N; ++k){++count;}printf("%d\n", count);
}

Func4中只有一个循环,循环N次,时间复杂度就是O(N)

//计算strchr的时间复杂度
const char*strchr(const char *str,int charcter);

最坏情况是O(N),最好的情况是O(1)

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

冒泡排序中,两层循环,外层循环控制每次遍历的范围,内层粗那混执行相邻的元素的比较和交换

  • 外层循环的迭代次数为n,其中n是数组的长度
  • 内层的迭代次数随外层循环的进行而减少,但是最坏情况下,内层迭代的次数也是n

因此,此方法的冒泡排序的时间复杂度为O(n^2)

long long Fac(size_t N)
{if (N == 0){return 1;}return Fac(N - 1) * N;
}

总共递归N次,数学表达式可写为F(N)=N+(N-1)+(N-2)+......+(1)+(0),时间复杂度就为O(N)

long long Fib(size_t N)
{if (N < 3){return 1;}return Fib(N - 1) + Fib(N - 2);
}

  • 在每次的递归调用中,函数都会进行一次加法操作
  • 递归的深度取决于输入参数N的大小 

数学表达式可写作F(N)=2^(N-2),时间复杂度就为O(2^N)(关于斐波那契数列的时间复杂度,深入探讨见:http://递归求解斐波那契数列的时间复杂度——几种简洁证明 - SleepyBag的文章 - 知乎 https://zhuanlan.zhihu.com/p/257214075)

2.空间复杂度 

I.空间复杂度概念

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

空间复杂度不是程序占用了多少bytes的空间,因为这个没有实际意义,所以空间复杂度是变量的个数,空间复杂度计算规则基本跟时间复杂度类似,也是采用大O渐近表示法。

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

就像时间复杂度的计算不考虑算法所使用的空间大小一样,空间复杂度也不考虑算法运行需要的时间长短,空间复杂度计算的是算法中额外的空间

II.示例:

void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int tmp = 0;for (size_t i = 0; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);tmp = 1;}}if (tmp == 0)break;}
}

在冒泡排序中,变量的个数只有end、tmp、i三个,是确定的,所以空间复杂度是O(1)(数组中的空间不计入,不属于算法中额外开辟的空间)

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,综上,空间复杂度为O(N)

long long Fac(size_t N)
{if (N == 0){return 1;}return Fac(N - 1) * N;
}

上述代码并没有创建变量,但是每次函数的调用都需要创建函数栈帧,这个算法调用了N次(具体取决于递归深度),所以空间复杂度是O(N)

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

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

相关文章

2024年第七届大数据技术国际会议(ICBDT 2024)即将召开!

2024年第七届大数据技术国际会议&#xff08;ICBDT 2024&#xff09;将于2024年9月20-22日在中国杭州的浙江工商大学举行。数据驱动未来&#xff0c;技术引领潮流。从数据挖掘算法的优化&#xff0c;到数据处理速度的提升&#xff0c;再到数据安全与隐私保护的进步&#xff0c;…

Scikit是什么?

目录 一、Scikit是什么&#xff1f; 二、用Scikit做一个简单房价预测例子 三、sklearn知识点 一、Scikit是什么&#xff1f; Scikit就是scikit-learn&#xff0c;是一个免费软件机器学习库。 https://scikit-learn.org/stable/https://scikit-learn.org/stable/ 用于预测数…

SoundStream: 下一代的神经网络音频编解码器,实时压缩不牺牲音质

音频编解码技术的目标是&#xff0c;通过减少音频文件的大小来节省存储空间或减轻网络传输的负担。理想的情况下&#xff0c;即使音频被压缩&#xff0c;我们听到的声音与原版也应该没有任何区别。 过去&#xff0c;已经有不少编解码技术被开发出来&#xff0c;满足了这些需求…

Day13-JavaWeb开发-事务管理(回顾/进阶)AOP基础(入门/概念)AOP进阶(通知类型/顺序/切点表达式)AOP案例

1. 事务管理 1.1 事务管理-事务回顾 1.2 事务管理-事务进阶 rollbackFor propagetion 2. AOP基础 2.1 AOP基础-快速入门 2.2 AOP基础-核心概念 3. AOP进阶 3.1 AOP进阶-通知类型 3.2 AOP进阶-通知顺序 3.3 切入点表达式-execution 3.4 切入点表达式-annottation 3.5 AOP进阶…

如何构建用于从收据中提取信息的生成式人工智能工具

原文地址&#xff1a;how-to-build-a-generative-ai-tool-for-information-extraction-from-receipts 使用 LangChain 和 OpenAI 工具从 Google Drive 中存储的收据图像中提取结构化信息 2024 年 4 月 10 日 纸质收据有各种样式和格式&#xff0c;是自动信息提取的一个有趣目…

Web服务器手动配置

目录 配置环境 http配置 配置步骤 1、首先安装Nginx&#xff08;已经安装的跳过这步&#xff09; 2、查看一下下Nginx的配置文件结构&#xff0c;了解如何配置&#xff0c;以及配置的各个条目有什么作用&#xff08;为接下来的配置打基础&#xff09; 3、创建你的网页 4、…

【JAVA项目】基于ssm的协同过滤算法的【图书推荐系统】

技术简介&#xff1a;采用B/S架构、ssm 框架、Java技术、MySQL等技术实现。 系统简介&#xff1a;系统权限按管理员和用户这两类涉及用户划分。&#xff08;1&#xff09;管理员功能需求 管理员登陆后&#xff0c;主要包括首页、个人中心、用户管理、书籍管理、书籍分类管理、热…

牛客NC382 切割木头【中等 二分超找 Java/Go/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/707d98cee255448c838c76918a702be0 核心 二分查找Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可…

安卓 app icon大小 安卓app界面尺寸大小

移动应用的界面设计画布尺寸设计多大&#xff08;特别是Android&#xff09;、图标和字体大小怎么定、需要设计多套设计稿么、如何切图以配合开发的实现&#xff1f; 本篇将结合iOS和android官方的设计规范、搜集的资料以及工作中的摸索&#xff0c;来分享移动应用界面设计中的…

gcc编译器分析

gcc编译器分析 参考词法分析语法分析预读一个符号语法分析函数调用关系重点函数分析c_parse_filec_parser_translation_unit 参考 《gcc源码分析》 词法分析 词法分析的过程就是将源代码识别成一个一个的词法符号&#xff0c;并在词法分析的过程中创建一些树节点&#xff0c…

tomcat+nginx 动静分离

一、单机反向代理 7-1 7-2 测试 二、多机反向代理 1. 环境准备 机器IP地址服务7-1172.168.1.11nginx7-2172.168.1.12tomcat7-3172.168.1.13tomcat 2. 配置7-1 Nginx 服务器 vim /apps/nginx/conf/nginx.confhttp:upstream tomcat {server 172.168.1.12:8080;server …

探索设计模式的魅力:分布式模式让业务更高效、更安全、更稳定

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索分布式模式之旅✨ 在数字化时代&#xff0c;企业面临着前所未有的挑战和机遇。…

STM32开发笔记-新建标准库工程

1.STM32开发方式 STM32开发一般包括三种方式&#xff1a;基于寄存器开发、基于标准外设库开发、基于HAL库开发。 标准外设库是最基础的STM32开发方式&#xff0c;提供了一系列函数用于配置和控制STM32的外设&#xff0c;如GPIO、USART、SPI等。使用标准外设库需要手动编写代码…

(39)4.29数据结构(栈,队列和数组)栈

#include<stdlib.h> #include<stdio.h> #define MaxSize 10 #define Elemtype int 1.栈的基本概念 2.栈的基本操作 typedef struct { Elemtype data[MaxSize]; int top; }Sqstack;//初始化栈 void InitStack(Sqstack& S) { S.top -1; //初始化…

Golang日志管理:使用log/slog实现高级功能和性能优化

Golang日志管理&#xff1a;使用log/slog实现高级功能和性能优化 简介基础使用初始化和配置日志级别 高级技巧自定义日志格式器条件日志处理 实战案例场景一&#xff1a;API请求日志记录场景二&#xff1a;错误跟踪和用户通知 性能优化优化日志记录的性能异步日志处理选择合适的…

Vue的项目启动指令分析

通过Vue CLI脚手架创建的项目&#xff0c;默认的启动项目方式是 npm run serve 这里的serve是可以修改的。 在创建的项目目录中&#xff0c;找到package.json 双击打开&#xff0c;找到scripts部分 在scripts部分&#xff0c;有一个"serve"键值对&#xff0c;这里的…

机器学习-K近邻算法(KNN)

目录 什么是KNN算法 图解KNN基本算法 &#xff08;1&#xff09;k近邻算法中k的选取 &#xff08;2&#xff09;距离函数 &#xff08;3&#xff09;归一化处理 &#xff08;4&#xff09;概率kNN KNN算法的优缺点 优势 缺点 KNN算法总结 什么是KNN算法 k近邻算法&…

[Spring Cloud] (6)gateway整体加解密

文章目录 简述整体效果后端增加配置nacos增加配置GlobalConfig 添加请求整体解密拦截器DecryptionFilter添加响应整体解密拦截器EncryptionFilter 前端请求拦截器添加整体加密逻辑请求头中添加sessionId 响应拦截器添加整体解密逻辑 简述 本文网关gateway&#xff0c;微服务&a…

[C语言]指针进阶详解

指针是C语言的精髓所以内容可能会比较多&#xff0c;需要我们认真学习 目录 1、字符指针 2、指针数组 3、数组指针 3.1数组指针的定义 3.2&数组名vs数组名 3.3数组指针的使用 4、数组传参和指针传参 4.1一维数组传参 4.2二维数组传参 4.3一级指针传参 4.4二级指…

学习如何使用PyQt5实现notebook功能

百度搜索“pyqt5中notebook控件”&#xff0c;AI自动生成相应例子的代码。在 PyQt5 中&#xff0c;QTabWidget 类被用作 Notebook 控件。以下是一个简单的示例&#xff0c;展示如何创建一个带有两个标签的 Notebook 控件&#xff0c;并在每个标签中放置一些文本。 import sys f…