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;
 else
 return 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.mfbz.cn/a/370157.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI新工具(20240204)pot-desktop - 为用户提供便捷的文字翻译和识别功能;ChatALL - 能够同时向多个AI机器人发送提示

pot-desktop - 为用户提供便捷的文字翻译和识别功能 pot-desktop pot-desktop是一款备受欢迎的跨平台划词翻译和OCR软件&#xff0c;为用户提供便捷的文字翻译和识别功能。 功能点&#xff1a; 划词翻译&#xff1a;用户只需将鼠标光标悬停在需要翻译的文字上&#xff0c;po…

028 方法的重载

方法重载的定义 使用案例 public static void main(String[] args) {// 匹配到max(int a, int b)System.out.println(max(1, 3));// 匹配到max(double a, double b)System.out.println(max(1L, 3L));// 匹配到max(double a, double b, double c, double d)&#xff0c;int自动…

3D 转换

1&#xff0c;3D的特点&#xff1a; 近小远大 物体后面遮挡不可见 2&#xff0c;3D移动 translate3d 3D移动在2D移动的基础上多加了一个可以移动的方向&#xff0c;就是z轴方向 transform&#xff1a;translateX&#xff08;100px&#xff09;&#xff1a;仅仅是在x轴上移动…

MySQL查询缓存

MySQL查询缓存 MySQL在查询的时候首先会查询缓存&#xff0c;如果缓存命中的话就直接返回结果&#xff0c;不需要解析sql语句&#xff0c;也不会生成执行计划&#xff0c;更不会执行&#xff1b;如果没有命中缓存&#xff0c;则再进行SQL解析以及进行查询&#xff0c;并将结果返…

steam搬砖项目赚钱吗?操作流程看这一篇就够了

很多人应该听说过steam&#xff0c;它是国外一款知名的游戏社交平台&#xff0c;也是目前世界上最大的游戏平台之一。而steam搬砖项目&#xff0c;关键就是靠信息差。我们要做的就是在steam以低价买入道具装备&#xff0c;然后上架到网易buff卖出&#xff0c;赚取差价。 什么人…

mac下载工具:JDownloader 2 for Mac 中文版

JDownloader是一款开源的下载管理工具&#xff0c;主要使用Java编程语言开发&#xff0c;因此它能够在支持Java的操作系统上运行&#xff0c;包括Windows、Linux和Mac OS。这款软件专门为那些需要通过网盘下载文件的用户提供便利&#xff0c;它支持众多流行的网盘服务&#xff…

机器学习 | 解析聚类算法在数据检测中的应用

目录 初识聚类算法 聚类算法实现流程 模型评估 算法优化 特征降维 探究用户对物品类别的喜好细分(实操) 初识聚类算法 聚类算法是一种无监督学习方法&#xff0c;用于将数据集中的对象按照相似性分组。它旨在发现数据中的内在结构和模式&#xff0c;将具有相似特征的数据…

MySQL运维实战(5.3) MySQL数据乱码的一些情况

作者&#xff1a;俊达 表数据乱码 表数据出现乱码的情况通常是由于数据的真实编码与相关参数不一致引起的&#xff0c;其中包括常见的参数如character_set_client、character_set_results、字段编码以及终端编码等。确保这些参数保持一致&#xff0c;可以有效预防和解决乱码问…

【Web】CVE-2021-22448 Log4j RCE漏洞学习

目录 复现流程 漏洞原理 复现流程 启动HTTP->启动LDAP->执行Log4j vps起个http服务,放好Exploit.class这个恶意字节码 LDAPRefServer作为恶意LDAP服务器 import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import javax.ne…

C++ 动态规划 线性DP 最长共同子序列

给定两个长度分别为 N 和 M 的字符串 A 和 B &#xff0c;求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。 输入格式 第一行包含两个整数 N 和 M 。 第二行包含一个长度为 N 的字符串&#xff0c;表示字符串 A 。 第三行包含一个长度为 M 的字符串&#xff0c;表…

程序员可以考取哪些证书更有用

IT行业有哪些证书 IT行业有许多证书可以考取&#xff0c;以下是一些主要的和有价值的证书相关信息&#xff1a; IT行业常用证书一览表 认证机构认证领域证书名称能力概述思科认证网络工程师CCNA、CCNP和CCIE等不同级别思科公司颁发的网络开发运维架构能力微软认证系统开发工程…

爬虫-网络空间微博信息管理系统的设计与实现-计算机毕业设计源码85633

摘 要 本论文主要论述了如何使用django框架开发一个网络空间微博管理信息系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述该系统的当前背景以及系统开发的目的&#xff0c;后续章节…

【C/C++ 11】贪吃蛇游戏

一、题目 贪吃蛇游戏机制是通过控制蛇上下左右移动并吃到食物得分。 蛇头碰到墙壁或者碰到蛇身就游戏结束。 食物随机生成&#xff0c;蛇吃到食物之后蛇身变长&#xff0c;蛇速加快。 二、算法 1. 初始化游戏地图并打印&#xff0c;地图的边缘是墙&#xff0c;地图的每个坐…

19.HarmonyOS App(JAVA)依赖布局DependentLayout使用方法

layout/ability_main.xml 显示位置不对&#xff1a;检查布局文件ohos:lef_of "id:tuzi",比如显示在兔子的左侧&#xff0c;这里就会显示不对。 需要id前没有$符号。改为&#xff1a; ohos:lef_of "$id:tuzi" <?xml version"1.0" encodi…

服务器学习

云服务器通常是通过多台物理服务器协同工作来提供的。云服务提供商使用大规模的数据中心&#xff0c;这些数据中心包含许多物理服务器。这些物理服务器上运行着虚拟化技术&#xff0c;允许它们被分割成多个虚拟服务器实例。 当用户请求创建一个云服务器时&#xff0c;云服务提…

FreeCAD的python脚本编写

简介 FreeCAD是一款强大的开源CAD软件&#xff0c;可以与python无缝对解&#xff0c;使用python来驱动三维几何的构建&#xff0c;具有很高的灵活性。本文主要讨论一下录制宏的方法&#xff0c;以及如何驱动特定参数 方法 打开FreeCAD软件&#xff0c;点击录制宏按钮后&…

C++实现鼠标点击和获取鼠标位置(编译环境visual studio 2022)

1环境说明 2获取鼠标位置的接口 void GetMouseCurPoint() {POINT mypoint;for (int i 0; i < 100; i){GetCursorPos(&mypoint);//获取鼠标当前所在位置printf("% ld, % ld \n", mypoint.x, mypoint.y);Sleep(1000);} } 3操作鼠标左键和右键的接口 void Mo…

什么是功能安全?

前言 在上一家公司的时候&#xff0c;有幸参加过公司内部的技术分享会&#xff0c;有一个同事跟我们分享了功能安全的一些内容。在提问环节&#xff0c;我问了一个问题“什么是功能安全&#xff1f;”他回答不上来。这也是我们很多人在工作中常犯的一个问题&#xff1a;我们做了…

汽车租赁系统

目录 一.研究背景 二.系统架构 1、SSM 2、JAVA 3、MySQL 4、系统架构 三.系统功能 1、车辆管理 2、客户管理 3、销售管理 4、统计分析 四.系统实现 五.结论总结 一.研究背景 传统的销售与信息统计管理都主要依靠人工&#xff0c;处理出的销售数据量与使用管理系统…

vcruntime140.dll有什么作用?vcruntime140.dll缺失的解决方法分享

解决因缺少vcruntime140.dll文件引起的问题实际上是相对简单的尽管最近有许多人在抱怨该文件频繁丢失且不知道该如何处理。作为一个责任编辑&#xff0c;我认为有很大的必要向大家清楚地解释一下。让我们从探索vcruntime140.dll文件缺少的修复方法吧。 一.msvcp140.dll的作用 …