DS:时间复杂度和空间复杂度

                                                         创作不易,感谢三连!

一、算法

1.1 什么是算法

     算法(Algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果。

1.2 算法的效率

     算法在编写成可执行程序的时候,运行的时候需要耗费时间资源和空间资源。因此衡量一个算法的效率,就是从时间和空间两个维度来衡量的,我们把他细分出了两个概念——时间复杂度和空间复杂度。

     时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。也就是说,现如今的我们判断算法的好坏重点是判断他的时间复杂度,在条件允许的情况下,我们也会非常乐意用空间去换时间。

二、时间复杂度

2.1 时间复杂度的概念

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

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

// 请计算一下Func1中++count语句总共执行了多少次?
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);
}

Func1时间复杂度:F(N)=N^2+2*N+10

N = 10时,F(N) = 130
N = 100时,F(N) = 10210
N = 1000时,F(N) = 1002010

当N取越大时,2*N以及10对F(N)的影响越来越小,而影响最大的是N^2,所以引入了大O的渐进表示法,即计算一个大概的次数就行。

2.2 大O的渐进表示法

大O符号(Big O notation):是用于描述函数渐进行为的数学符号。
推导大O阶方法:
1、用常数1取代运行时间中的所有加法常数。(函数中只有常数)
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
使用大O的渐进表示法以后

Func1的时间复杂度为:O(N)

N = 10时,F(N) = 100
N = 100时,F(N) = 10000
N = 1000时,F(N) = 1000000

大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。

另外有些算法的时间复杂度存在最好、平均和最坏情况:
 最坏情况:任意输入规模的最大运行次数(上界)
 平均情况:任意输入规模的期望运行次数
 最好情况:任意输入规模的最小运行次数(下界)
例如:在一个长度为N数组中搜索一个数据x
 最好情况:1次找到
 最坏情况:N次找到
 平均情况:N/2次找到
在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)

2.3 为什么要考虑最坏情况(卡瑞尔公式)

        我是这样理解的:时间复杂度也是人为设计的,参考了心理学上的卡瑞尔公式,即"接收最坏的,往往才能有最好的"

卡瑞尔公式:强迫自己接受最坏的情况,首先在精神上接受它,然后集中精力从容解决问题,从根本上抹除忧虑,甚至有时候能给你带来惊喜。

最坏情况下的时间复杂度是算法在任何输入实例上运行时间的界限,这就保证了算法的运行时间不会比最坏情况更长 。

三、空间复杂度

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

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

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

四、常见的复杂度对比

五、时间复杂度和空间复杂度例题

特点:时间一去不复返,但是空间可以重复利用!!

// 计算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)

// 计算Func4的时间复杂度?
void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++ k){++count;}printf("%d\n", count);
}

O(1)

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{if(0 == N)return 1;return Fac(N-1)*N;
}

每次调用函数都是O(1)的复杂度,调用N次就是O(N)的复杂度

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{if(0 == N)return 1;for(int i=0;i<N;++i)
{
……
}return Fac(N-1)*N;
}

递归函数,第一次执行了N次循环,第二次执行N-1次循环,以此类推,最后执行N次时结束,所以调用总次数为等差数列,求和N(N+1)/2,时间复杂度是O(N^2)

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

最左侧会逐步减少到Fib(1),有N层,但是右侧未必能走到N层,所以呈现的三角形并不是等腰三角形。但是不影响大O阶表示时间复杂度O(N^2)

时间一去不复返,但是空间是可以重复利用的,新销毁的函数栈帧释放后可以马上被新的函数栈帧替代,重复利用的空间,所以空间复杂度是O(N) 

// 计算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;}
}

嵌套for循环,所以时间复杂度是O(N^2),虽然每次循环都有存在创建i和end变量,但其实使用的都是一块空间,空间一直在被重复利用,所以空间复杂度O(1)

六、二分查找法 

6.1 时间复杂度

// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{assert(a);int begin = 0;int end = n-1;// [begin, end]:begin和end是左闭右闭区间,因此有=号 while (begin <= end){int mid = begin + ((end-begin)>>1);if (a[mid] < x)begin = mid+1;else if (a[mid] > x)end = mid-1;elsereturn mid;}return -1;
}

如上图,空间复杂度是logN

 6.2 效率以及实用性

7、内存、外存、CPU、缓存的一些相关知识

7.1 内存和外存的区别

内存:快、小、8G-16G左右、带电存储

外存:慢、大、500G左右、不带电存储

      cpu只能在内存访问,要想访问外存就得先把数据拿到内存中去,运行速度会比较慢,所以我们平时处理数据都是在内存中处理的,处理之后要存储时才会拿到外存中保存起来,这其实和文件操作很类似,文件也是属于外存,可以永久化地保存数据。

      举个例子:我们打开word写论文,在word还没保存的时候,该数据是存储在内存的缓存中的,如果这个时候突然断电,那么数据在缓存中没有及时保存到外存里,就会造成数据丢失,而如果我们保存在外存里,即使断电也不会出现数据丢失。

7.2 数据结构和数据库

      我们学习数据结构的本质意义,是帮助我们在内存中管理数据,而因为不同的数据结构有不同的特点,对应着不同的需求,所以没有一种数据结构可以完美的解决所有的问题,因此需要学习大量的数据结构类型,根据场景和需要去使用

     而我们在外存中管理数据就是通过数据库、文件。

7.3 CPU、寄存器、三级缓存

一般我们CPU在访问内存数据的时候,需要优先将数据放在寄存器或者三级缓存中。

寄存器是最快的,但是一般只有4-8字节的大小,对于大一点的数据,一般都是加载到缓存中再由cpu进行读取。

缓存命中率:在说明这两个问题之前。我们需要要解一个术语 Cache Line。缓存基本上来说就是把后面的数据加载到离自己近的地方,对于CPU来说,它是不会一个字节一个字节的加载的,因为这非常没有效率,一般来说都是要一块一块的加载(有利于提高缓存命中率)的,对于这样的一块一块的数据单位,术语叫“Cache Line”,一般来说,一个主流的CPU的Cache Line 是 64 Bytes(也有的CPU用32Bytes和128Bytes),64Bytes也就是16个32位的整型,这就是CPU从内存中捞数据上来的最小数据单位。

所以cpu在读取数据的时候,如果在缓存中找到该数据,就可以直接处理,这种情况就是缓存命中率高。而如果在缓存中找不到该数据,那么就需要先从内存中加载到缓存里再读取数据,这种情况就是缓存命中率低。

对于数组而言,由于其连续存放的特点,CPU在访问第一个数据的时候,会顺便把后面的数据加载进缓存,而CPU访问第二个数据的时恰好第二个数据就在缓存,甚至可能第三个、第四个数据都在缓存(取决于cpu的处理数据容量),所以数组(顺序表)的缓存命中率高!而对于链表来说,各个结点直接在物理结构上不存在连续,所以即使cpu加载了后续的空间,大概率也是无用的,所以链表的缓存命中率低。并且无用的数据还挤占了原先缓存区的位置,容易造成缓存污染。

八、顺序表和链表的再总结

顺序表

优点:1、下标随机访问(排序、二分查找)

           2、cpu高速缓存命中率高

缺点:1、指定位置插入和删除元素效率低下

           2、扩容存在效率损失,还可能存在一定的空间浪费

应用场景:适用于高效存储以及频繁访问的场景

链表

优点:1、任意位置插入和删除效率都高

           2、按需申请和释放,不存在空间的浪费

缺点:1、不支持下标的随机访问

           2、cpu告诉缓存命中率低

应用场景:适用于频繁任意位置插入和删除的场景

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

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

相关文章

Qt---资源文件添加

Qt—资源文件添加 Qt 资源系统是一个跨平台的资源机制&#xff0c;用于将程序运行时所需要的资源以二进制的形式存储于可执行文件内部。如果编写的程序需要加载特定的资源&#xff0c;那么将其放置在资源文件中&#xff0c;便不需要担心文件的缺失。 1 需求 为菜单栏设置图标&…

【OpenCV人脸检测】写了个智能锁屏小工具!人离开电脑自动锁屏

文章目录 1. 写在前面2. 设计思路3. 人脸检测4. 程序实现 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向感兴趣的朋…

人体生物钟程序设计(C语言)

前几年在本站发布过博文介绍人体生物钟程序的制作方法。后来发现上传后显示的博文有错漏&#xff0c;计算符号脱漏。这会误导读者。今修订整理重新发布&#xff0c;展示一下漂亮的界面设计。 人体生物钟也就是人体生物节律。人体生物节律是自然进化赋予生命的基本特征之一&…

基于uniapp+vue酒店宾馆客房民宿管理系统设计 微信小程序_54ybz

APP性能需求 &#xff08;1&#xff09;顾客在安卓APP页面各种操作可及时得到反馈。 &#xff08;2&#xff09;该平台是提供给多个用户使用的平台&#xff0c;用户使用之前需要注册登录。登录验证后&#xff0c;用户才可进行各种操作[10]。 &#xff08;3&#xff09;管理员、…

Redis客户端有哪些:你了解吗?

一、分类 Redis客户端工具是用来连接和管理redis服务器的软件&#xff0c;它们可以有不同的类型&#xff0c;如桌面客户端、web客户端和IDE插件。不同的客户端工具有各自的优缺点和特色&#xff0c;你可以根据你的需求和喜好选择合适的工具。 1、Redis 命令行工具 redis-cli官…

【洛谷】P1434滑雪

#include<iostream> #include<string.h> using namespace std;const int N 310; int h[N][N]; int f[N][N]; int n,m;int dx[4] {1,0,-1,0}, dy[4] {0,-1,0,1};int dp(int x,int y) {// 如果点(x,y)已经去到过了&#xff0c;直接返回结果就行if(f[x][y] ! -1) r…

sqli.labs靶场(29到40关)

29、第二十九关 id1 id1 尝试发现是单引号闭合&#xff0c; -1 union select 1,2,3-- -1 union select 1,2,database()-- -1 union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schemasecurity)-- -1 union select 1,2,(select…

Python对日期的一些操作

1. 把这种日期 Mon Jan 29 11:10:49 0800 2024 转换成 ‘2024/2/1 10:50:38’ 这里定义一个func 传入英文日期&#xff0c;返回标准日期格式 def time_formater(input_time_str): input_format %a %b %d %H:%M:%S %z %Y output_format %Y-%m-%d %H:%M:%S return dat…

69.请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?

69.请描述Spring MVC的工作流程&#xff1f;描述一下 DispatcherServlet 的工作流程&#xff1f; 核心架构的具体流程步骤如下&#xff1a; 首先用户发送请求——>DispatcherServlet&#xff0c;前端控制器收到请求后自己不进行处理&#xff0c;而是委托给其他的解析器进行…

别瞎写工具类了,Spring自带的不香吗?

前言 最近有些小伙伴&#xff0c;希望我分享一些好用的工具类&#xff0c;帮他们提升开发效率。 今天这篇文章专门跟大家一起总结一下&#xff0c;Spring框架本身自带的一些好用的工具类&#xff0c;希望对你会有所帮助。 1 Assert 很多时候&#xff0c;我们需要在代码中做判…

C#验证字符串是否包含汉字:用正则表达式 vs 用ASCII码 vs 用汉字的 Unicode 编码

目录 一、使用的方法 1.使用正则表达式验证字符串 2.使用正则表达式验证字符 3.用ASCII码判断 4.用汉字的 Unicode 编码范围判断 二、实例 1.源码 2.生成效果 验证一个字符串是否是纯汉字或者包含有汉字的前提&#xff0c;是VS编辑器的默认编码格式设置为&#xff1a;选…

Unity animator 动画实现指定时间开始播放

在我们使用Unity帧动画时&#xff0c;如用到同一个帧动画的部分动画&#xff0c;那么我们可以考虑用指定播放时间的方法实现。 如我在场景中创建一个2D帧动画&#xff0c;并创建一个2D对象使用该帧动画。 然后复制该2D对象&#xff0c;并创建一个控制脚本GameController1.cs&a…

Logback学习

logback 1、logback介绍 Logback是由log4j创始人设计的另一个开源日志组件&#xff0c;性能比log4j要好。 lockback优点&#xff1a; 内核重写、测试充分、初始化内存加载更小&#xff0c;这一切让logback性能和log4j相比有诸多倍的提升。logback非常自然地直接实现了slf4j…

聊聊并发编程,另送5本Golang并发编程新书

大家好&#xff0c;我是飞哥&#xff01; 并发编程并不是一个新话题&#xff0c;但是我觉得在近几年以及未来的时间里&#xff0c;并发编程将显得越来越重要。 为什么这样讲&#xff0c;让我们先回到一个基本的问题上来&#xff0c;为什么我们要采用并发编程&#xff1f;关于这…

【深度学习】讲透深度学习第3篇:TensorFlow张量操作(代码文档已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论深度学习相关知识。可以让大家熟练掌握机器学习基础,如分类、回归&#xff08;含代码&#xff09;&#xff0c;熟练掌握numpy,pandas,sklearn等框架使用。在算法上&#xff0c;掌握神经网络的数学原理&#xff0c;手动实…

LLaMA 模型中的Transformer架构变化

目录 1. 前置层归一化&#xff08;Pre-normalization&#xff09; 2. RMSNorm 归一化函数 3. SwiGLU 激活函数 4. 旋转位置嵌入&#xff08;RoPE&#xff09; 5. 注意力机制优化 6. Group Query Attention 7. 模型规模和训练超参数 8. 分布式模型训练 前置归一化与后置…

PHP面试问题与简答

问题与简答 PHP 篇 echo、print、print_r、var_dump 区别 echo和print是语言结构、print_r和var_dump是普通函数 echo&#xff1a;输出一个或多个字符串 print&#xff1a;输出字符串 print_r&#xff1a;打印关于变量的易于理解的信息 var_dump&#xff1a;打印关于变量的…

适合大学英语搜题的软件?如何选择一款好用的大学搜题工具? #职场发展#微信#学习方法

大学生必备的搜题工具&#xff0c;专业课本习题、电子版教材、考研资料、英语四六级等考试题目也能一并搜索&#xff0c;每道题目都有详细的讲解&#xff0c;每个都堪称大学神器。 1.颐博咨询 这是一个网站 找题好用的在线搜题站,快考不限次搜题助手,问题截图搜题软件,练题通…

【文本到上下文 #8】NLP中的变形金刚:解码游戏规则改变者

一、说明 欢迎来到我们对不断发展的自然语言处理 &#xff08;NLP&#xff09; 领域的探索的第 8 章。在本期中&#xff0c;我们将重点介绍一项重塑 NLP 格局的突破性创新&#xff1a;Transformers。在我们之前对 seq2seq 模型、编码器-解码器框架和注意力机制的讨论之后&#…

17.Golang channel的基本定义及使用

目录 概述实践无缓冲 channel代码结果 缓冲 channel代码结果 channel的关闭特点代码结果range代码结果 select channel代码结果 结束 概述 此篇文章介绍 channel 的用法 无缓冲 channel缓冲 channelchannel的关闭特点range channelselect channel 每一种&#xff0c;配上完整…