草庐IT

【数据结构】算法的时间复杂度和空间复杂度(下)(附leetcode练习题)

fighting小泽 2023-08-26 原文

☃️个人主页:fighting小泽
🌸作者简介:目前正在学习C语言和数据结构
🌼博客专栏:数据结构
🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻

1. 空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用的额外的存储空间大小的量度 。
空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。
空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。
注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。

1.1 空间复杂度的例子

实例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(N),有的会觉得空间复杂度是 O(1)。为什么会觉得是O(N)呢,因为这里有一个数组,这个数组有N个空间。但是数组的N个空间算不算是冒泡排序的消耗?

其实是不算的,因为这个数组存储N个数据,我们对它进行排序其实是对数组的内容进行处理,它本身就要有,不是因为我们要排序而自己开的空间,在冒泡排序里面创建的end和exchange是常数个,所以它的空间复杂度是O(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;
}

正常的斐波那契数列是三个变量来回倒,它的空间复杂度就是O(1),但是我们这里是malloc了一个数组,这个数组有N+1个空间,所以它的空间复杂度就是经典的O(N)。

实例3:

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

这个时候就涉及到一个栈帧的问题了,每次函数调用会建立一个函数栈帧,相当于建立了N个栈帧,那每个栈帧开辟多少空间呢?
每个栈帧里面其实只有常数个,但是因为创建了N个栈帧,所以它的空间复杂度是O(N)。


实例4:

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

它的时间复杂度是2 ^ N,可能大多数老铁觉得它的空间复杂度也是
2 ^ N,是一样的。

实际上它不是,它的空间复杂度是不好算的,它的空间复杂度是O(N)。
为什么呢?这时候大家就要看到一个问题,递归调用是咋调的?

斐波那契第N项是不是要调用(N-1)和(N-2)啊,那我问大家它是同时调用(N-1)和(N-2)项吗?不是,它会先调用(N-1),然后调用(N-1)下面的(N-2),然后调用下面的(N-3),会一直往下调用,调用到第2项之后才回来,再调用右边再回去,再调用右边再回去。那就意味着这个栈帧的建立是这样的,最多会建立多少个栈帧呢?是0到N-2个栈帧,就是N-1个栈帧。他会先往深不断不断去走,走了回来的时候栈帧就销毁了,再调用右边的会跟左边的重复用一个栈帧空间,那最多会建立多少层呢?N层。可以认为最多就建立左边的这一列,再调用右边的会跟左边的重复用一个栈帧空间。

这里有一句话送给大家:时间是一去不复返的,空间是可以重复利用的。时间是累计计算的,空间不累计计算。


这里我们再看一个代码:

void Func1()
{
	int a = 0;
	printf("%p\n", &a);
}
void Func2()
{
	int b = 0;
	printf("%p\n", &b);
}
int main()
{
	Func1();
	Func2();

	return 0;
}


我们发现,a和b是使用同一块空间的,这时因为函数调用时创立栈帧,函数结束时栈帧也会销毁,但是销毁的这块栈帧空间不是不能使用了,而是归还操作系统了,下一次函数栈帧空间也在这里创建,所以a和b的地址是一样的。同理,刚刚斐波那契数列销毁的空间也会被下一次函数调用所利用。

2.常见复杂度对比

3.leetcode练习题

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。189 . leetcode - 旋转数组

1. 暴力求解,旋转K次

我们可以用一个 tmp 记录下最后一个元素,然后进行for循环,把每个元素向右移动一位,再把 tmp 传给第一个元素就行了。大家可以自己试一试,不够这样写在力扣过不去,会超时。

2. 三段逆置

这是个聪明人才能想出来的方法,先把前 N-K 个元素逆置,再把后 K 个逆置,再把整体逆置就完成了

void swap(int* a, int* b) {
    int t = *a;
    *a = *b, *b = t;
}

void reverse(int* nums, int start, int end) {
    while (start < end) {
        swap(&nums[start], &nums[end]);
        start += 1;
        end -= 1;
    }
}

void rotate(int* nums, int numsSize, int k) {
    k %= numsSize;
    reverse(nums, 0, numsSize - 1);
    reverse(nums, 0, k - 1);
    reverse(nums, k, numsSize - 1);
}

3. 空间换时间

我们可以创建一个新数组,把前 N-K 个元素放到后面,把后 K 个元素放到前面

注意:当 K 大于numsSize的时候相当于把数组转过了一遍,但是直接写 K 会越界,访问到后面的元素,所以我们可以令 k = (i+k)%numsSize

void rotate(int* nums, int numsSize, int k) {
    int newArr[numsSize];
        for (int i = 0; i < numsSize; i++)
    {
        newArr[(i+k)%numsSize] = nums[i];
    }
    for (int i = 0; i <numsSize; i++)
    {
        nums[i]=newArr[i];
    }
}

结尾

这些就是我给大家分享的关于算法的复杂度的知识啦,希望我们都能有所收获!
先赞后看,养成习惯!!^ _ ^
码字不易,大家的支持就是我坚持下去的动力,点赞后不要忘了关注我哦!

如有错误,还请您批评改正(。ì _ í。)

有关【数据结构】算法的时间复杂度和空间复杂度(下)(附leetcode练习题)的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  4. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  5. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  8. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  9. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  10. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

随机推荐