数据结构篇:空间复杂度和时间复杂度

目录

1.前言:

 1.1 学习感悟

 1.2 数据结构的学习之路(初阶)

2.什么是数据结构和算法

2.1 数据结构和算法的关系

2.2 算法的重要性

2.3 如何衡量算法的好坏

3.时间复杂度

3.1 时间复杂度的概念

3.2 大O的渐进表示法 O()

4.空间复杂度

5. 常见的时间复杂度和空间复杂度计算

5.1 时间复杂度计算

5.2 空间复杂度计算

6.时间复杂度对比图

7.结语


 

1.前言:

        1.1 学习感悟

        本篇文章作为数据结构的开篇,也是对C语言部分结尾的继承,属于是承上启下,数据结构中目前使用的编译器还是Visual Studio 2022,在正式讲之前,说点篇外话,继上一篇动态内存的管理后,很久没有更新是因为博主的电脑出了问题,维修了小半个月,再者本人对于数据结构的学习周期比较长,数据结构的难度也相对于C语言高了一个层次,但是我觉得只要认真努力的学下去,武装自己,然后做对题目,那一刻,先前再怎么苦也值得!当你做对题目,在网站上提交你的代码通过的时候,肯定会有满满的成就感,不是吗?

        1.2 数据结构的学习之路(初阶)

        在数据结构的具体学习中,大致分为有:

  1. 对于时间复杂度和空间复杂度的一个基本了解
  2. 顺序表
  3. 链表
  4. 队列
  5. 二叉树
  6. 排序

        这7大类,数据结构基础篇主要内容就是这些,包括想要考研的朋友们,王道计算机408中的数据结构中,是线性表(顺序表,链表),栈,队列,数组,串,二叉树,查找,排序。大部分都能包含。如果想要考研的同学看到文章也有一丝丝的收获,这对我也是很大的鼓舞。在这里,我也不能说数据结构怎么学能学好,我也是在学习的过程中,但是经过了一段时间的初步学习,我认为有三点必须要做到,这样学数据结构不能说万无一失,只能说会学的很扎实,思路很顺畅,时间相对花的少很多。

  • 多画图,对于每个内容的学习都尽量画图,它能帮助你很好的构思代码的整体流程,甚至能够潜在地培养自己的算法素质,算法能力。
  • 多调试,在C语言学习过程中,应该都是能够学会如何基本的调试,这里就不过多赘述了,想了解基本方法请移步C语言初阶回顾-CSDN博客中有过相关介绍,在初步学习的时候可能会不重视调试,觉得我去百度一下,或者把自己的代码截图,复制给AI工具,(文心一言,deepseek)让着帮忙解析就好了,这是个方法,但不是最好的方法,ai会让你养成惰性,如果你学会了怎么去调试,那是自己成长的一步,如果再进一步,大部分由于疏忽导致的错误能够通过你亲手F10,F5,F11出来解决,那是一件多么舒服的事情,你可以通过自己而不是外部工具去解决,久而久之,你会爱上调试的,会出了bug第一时间不是想AI帮我一下,而是下意识F10,调试是一个基本功!在数据结构的学习中,调试是必不可少的,也是和画图同等重要的能力。
  • 了解函数栈帧空间,这在二叉树的学习中,是一个门槛,如果了解了函数栈帧的相关知识,学起来将事半功倍。像博主本人在学习的过程中,把那一块知识淡忘了,就会重新去温故一下,看看曾经写过的文章,从而知新。忘记正常,记起来就好了。

        进入正题

2.什么是数据结构和算法

        在互联网中,各大社交平台都在讨论数据结构的时候,总会和算法紧密相连,而且我看到过很多互联网大厂和中厂,算法岗位的学历起步是要硕士。算法要求之高,那么到底什么是数据结构,什么又是算法。这两者为什么紧密相连?

2.1 数据结构和算法的关系

        数据结构,在博主的看来,就是电脑在内存中对存储数据有不同的结构方式。而算法,就是解决一个或者多个问题的一系列计算步骤,用来把输入的数据转化为自己想要结果。所以算法我认为不单单是一种解决问题的方式,更多的还是思维

        而算法和数据结构的关系可以理解为互相的关系,数据结构给算法提供发挥作用的空间,算法为数据结构进行作用。没有数据结构,算法发挥不出它的作用,没有算法,数据结构就没有问题的解决这一步,两者共生。我在网络上也搜到我认为很不错的观点,大家可以看看:

2.2 算法的重要性

 2.3 如何衡量算法的好坏

        算法在对问题解决的过程中,或者说在计算机中运行的时候,是要消耗内存资源和空间的。以及要花费一定时间。那这里其实已经指出了衡量算法优劣的两个标准,这也是面试官常考的点:

        1.时间复杂度

        2.空间复杂度

        不知道你们有没有发现一个现象,或者看到这里想起来一些东西。在c语言的递归中,比如斐波那契数列,阶乘递归,一开始数字小还好,但当你输出一个较大的数字,斐波那契数列的第40,45,50,越往后计算机算的就越慢,而且打开任务管理器观察,运行该程序所占用的内存百分比会只多不少。 我以斐波那契数列来实测一下。

#include<stdio.h>int Fib(int n)
{if (2>=n){return 1;}else{return Fib(n - 2) + Fib(n - 1);}
}
int main()
{int a = 0;scanf("%d",&a);int ret =Fib(a);printf("%d\n",ret);return 0;}

       输入的值为30时:速度很快,瞬间出来。

时间复杂实测

       输入值为40时: 出来了,过了5秒,光标在闪动(由于博主尝试录屏,但是无法录制到cmd框,就不能放出视频演示了,手机录制效果不好,大家自行尝试)

        

         输入的值为45时,大概过了半分钟,才出结果,数字也确实非常大。

         说明用递归这个算法来解决斐波那契数列,数字大了,并不好,用for循环,会更好。计算时间就表示时间复杂度,内存资源的消耗就表示空间复杂度。那也不能每次都上机实测,所以就有了这两个概念。

3.时间复杂度

      3.1 时间复杂度的概念

        算法的时间复杂度是程序基本操作的执行次数,就是一个基本表达式,它在程序中运行了多少次,引入一个案例来讲:

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 A = 10;while (A--){count++;}

        上述过程中,在第一个for循环中,嵌套了一个for循环,那么在里面的执行次数,以未知数x来表示,就是,第三个for循环是单独的,是2,第三个就是常数10了,总次数是,但是真心要算出来吗,未知数小还好,x=1001,2345,54233这些复杂的数字,算起来未免过于繁琐。 实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数。那么这里我们使用大O的渐进表示法。

3.2 大O的渐进表示法 O()

        在我的笔记中是这样的记载的:

  1. 用常数1取代运行时间中的加法常数
  2. 保留最高阶项即可
  3. 最高阶次方不是1的话,把这个项的相乘系数删了。

        所以上面的表达式去掉常数,去掉非最高阶,就剩个了,那么规范的纠正我的错误,我刚刚是用数学表达式来写,正确的应该是把x换成N,所以是

        通过上面大O的渐进表示法去掉了那些对结果影响不大的项,表示出了执行次数。另外有些算法的时间复杂度存在最好、平均和最坏情况:你选择哪个?

        这里举个生活中的例子就明白了,你和你最好的朋友聚餐,或者工作有个重要的客户要约见,然而你手中的活还没干完,对方给你了下午5点,晚上7点和8点见面,你会选择什么时间。按理来说容错率最高的是8点吧,5点,万一还没忙完呢,7点,万一堵车呢,最迟的时间应该是最好的。你提前到了,那给人印象很好,卡点是正常,迟到就给人不好印象了。这就是最好,折中和最坏预期结果。为了心理预期,我们往往会选择最迟的点。这样很好理解了吧

        在算法中,在一个长度为N数组中搜索一个数据x:

最好情况:1次找到

最坏情况:N次找到

平均情况:N/2次找到

时间复杂度应该是

4.空间复杂度

        空间复杂度也是数学表达式,是算法在运行过程中占用内存空间大小的多少,但是说实话真去测算消耗了多少空间?不,算的是变量的个数,也是大O的渐进表示法 O(),依旧举个例子说明,用斐波那契数列来举例子:

#include<stdio.h>int Fib(int n)
{if (2>=n){return 1;}else{return Fib(n - 2) + Fib(n - 1);}
}

        计算有几个变量,也就是开辟了几个空间,由于n是不知道的,所以开辟了n个空间,空间复杂度为

5. 常见的时间复杂度和空间复杂度计算

5.1 时间复杂度计算

        例1:

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

        例2:

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

        例3:冒泡排序

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

        例4:二分查找

int BinarySearch(int* a, int n, int x)
{assert(a);int begin = 0;int end = n-1;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;
}

        例5:阶乘递归

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

        例6:斐波那契数列

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

        案例分析:


  1. 案例1,是2N+M,根据大O的渐进表示法,M是个常数,所以是
  2. 案例2,是M+N次,根据大O的渐进表示法,M,N都是未知的,所以是
  3. 案例3,其实是个冒泡排序,最好的情况就是它是个有序数组,比如{1,2,3,4,5,6.....,n},那么就只要排n-1次,最坏的情况,就是完全无序,第一趟排,n-1次,第二趟,n-2次,最后一趟,1次。总次数加起来就是(n-1)+(n-2)+(n-3)+.....+1,总和为,所以按最坏的打算时间复杂度O(N^2)
  4. 案例4,是个二分查找,最好1次,最坏O(logN)次,时间复杂度为 O(logN) ,logN在算法分析中表示是底数为2,对数为N。ps:二分查找的原理就是缩小一半查找,理想中很好很厉害,但现实中不可靠,因为只适用于有序数组。
  5. 阶乘算法,最好是1次,最坏是N次,时间复杂度为O(N)。
  6. 斐波那契数列,操作了2^N次,所以时间复杂度为O(2^N)。图解如下:

     

        不画图是很难看出来的,1生2,2生4,4生8,每一个诞生2个函数要执行 ,所以说画图很重要,不画图心里是很难凭空心算的。实践出真知

5.2 空间复杂度计算

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

        例2:阶乘递归

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

        例3 :斐波那契数列

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

案例分析:


  1. 例1冒泡排序函数内部,看看开辟了几个变量,数变量就好了,end,exchange,i,三个,是常数,所以空间复杂度为O(1);
  2. 例2递归调用了N次,开辟了N个栈帧,每个栈帧又会开辟另一个空间,而递归不结束,空间不归还,所以产生了FAC(N),FAC(N-1),FAC(N-2)........FAC(1),FAC(0)。这是个嵌套调用!!!,没有销毁掉,所以空间复杂度为O(N),不要以为单个栈帧空间里的变量只有一个N,没有别的变量,你要算N个空间里的变量数量。
  3. 例3是1分为2,然后2里面的两个递归用的同一块空间,是先调用n,然后调用n-1,一直调用到末尾2,符合if条件,返回了!注意开始归了!2的上一层是3,3下面的2已经调用了,所以会调用另一边1,1调用了,再返回3。然后发现3的1和2都计算过了,往上返回到4,依次往上,因为递归的过程是先递后归,递一边,递完返回再归另外一边,空间复杂度为O(N),这个案例注意调试,我是调试了很多次,看了很多次N的变化,画了递归流程图,才看明白,其实讲的再多,不如自己去好好的认真调试一遍,就很nice。

     

阶乘递归空间复杂度

 

 

6.时间复杂度对比图

        这是我从网络上找的两张关于时间复杂度随着个数的增大的对比图,(logn理想状态是很好的)其实时间复杂度要比空间复杂度复杂很多,空间复杂度大多都是O(1),O(N),而时间复杂度会多很多,大家也可以网上找找,大部分图都是一样的。

时间复杂度

         再者就是常见的时间复杂度参数了

7.结语

        本篇文章作为数据结构的开篇,就先讲这么多,时间复杂度和空间复杂度是贯穿整个数据结构的,另外后面的内容顺序表,链表等都涉及到动态开辟空间,所以C语言中的动态内存管理以及函数栈帧空间的学习是很必要的,因为顺序表,和栈都和通讯录的撰写逻辑有关联。记住一开始说的三点,画图,调试, 了解函数栈帧空间 It is imperative for you to learn!共同进步,就像gitee里说的,希望你的代码有朝一日能够像下面那张图片一样。

 

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

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

相关文章

node-ddk,electron,截屏封装(js-web-screen-shot)

node-ddk 截屏封装(js-web-screen-shot) https://blog.csdn.net/eli960/article/details/146207062 也可以下载demo直接演示 http://linuxmail.cn/go#node-ddk 感谢/第三方 本截屏工具, 使用的是: js-web-screen-shot https://www.npmjs.com/package/vue-web-screen-shot…

泰坦军团携手顺网旗下电竞连锁品牌树呆熊 共创电竞新纪元

在电竞行业的浪潮中&#xff0c;品牌之间的战略合作愈发成为推动市场前行的重要动力。最近&#xff0c;电竞显示器领域领军品牌泰坦军团高层领导出席顺网旗下电竞连锁品牌树呆熊十周年盛典。会议现场&#xff0c;双方高层领导宣布泰坦军团与树呆熊正式达成战略合作伙伴关系。 在…

HandyJSON原理

HandyJSON 的优势 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式, 应用广泛. 在 App 的使用过程中, 服务端给移动端发送的大部分都是 JSON 数据, 移动端需要解析数据才能做进一步的处理. 在解析JSON数据这一块, 目前 Swift 中流行的框架基本上是 SwiftyJSON, …

信号的产生和保存

信号的产生 信号就是操作系统对用户操作做出的反应&#xff0c;但它的本质就是往操作系统写入信号&#xff0c;这是由操作系统的结构决定的。通过修改比特位来告诉操作系统接收信号和传了几号信号。 也正是因为我们身为用户无法亲自修改内核数据&#xff0c;所以我们需要通过操…

在C++ Qt中集成Halcon窗口并实现跨平台兼容和大图加载

目录 1. Halcon窗口嵌入Qt Widget 2. 处理大图加载 3. 多线程优化显示 4. 跨平台兼容性 1. Halcon窗口嵌入Qt Widget 将Halcon的HWindow控件嵌入到Qt的QWidget容器中,利用系统原生句柄实现跨平台。 #include <HalconCpp.h> #include <QWidget>class HalconWi…

深度学习技术与应用的未来展望:从基础理论到实际实现

深度学习作为人工智能领域的核心技术之一&#xff0c;近年来引起了极大的关注。它不仅在学术界带来了革命性的进展&#xff0c;也在工业界展现出了广泛的应用前景。从图像识别到自然语言处理&#xff0c;再到强化学习和生成对抗网络&#xff08;GAN&#xff09;&#xff0c;深度…

蓝光三维扫描技术:汽车零部件检测的精准高效之选

——汽车方向盘配件、保险杠塑料件、钣金件检测项目 汽车制造工业的蓬勃发展&#xff0c;离不开强大的零部件制造体系作支撑。汽车零部件作为汽车工业的基础&#xff0c;其设计水平、制造工艺、质量控制手段逐渐与国际标准接轨&#xff0c;对于零部件面差、孔位、圆角、特征线…

数据库联表Sql语句建一个新表(MySQL,Postgresql,SQL server)

数据库联表Sql语句建一个新表(MySQL,Postgresql,SQL server) 如果你想基于 SELECT USERS.ID,USERS.NAME,USERS.EMAIL,USERS.ID_CARD,USERS.V_CARD,USERS.ADDRESS,v_card.type,v_card.amount FROM USERS JOIN v_card on USERS.V_CARDv_card.v_card 这个查询结果创建一个新表&am…

六十天前端强化训练之第三十天之深入解析Vue3电商项目:TechStore全栈实践(文结尾附有源代码)

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 深入解析Vue3电商项目&#xff1a;TechStore全栈实践 一、项目架构设计 二、核心功能实现 三、组合式API深度实践 四、性能优化实践 五、项目扩展方向 六、开发经验总结…

【人工智能】机器学习中的评价指标

机器学习中的评价指标 在机器学习中&#xff0c;评估指标&#xff08;Evaluation Metrics&#xff09;是衡量模型性能的工具。选择合适的评估指标能够帮助我们更好地理解模型的效果以及它在实际应用中的表现。 一般来说&#xff0c;评估指标主要分为三大类&#xff1a;分类、…

不同机床对螺杆支撑座的要求有哪些不同?

螺杆支撑座是机械设备中重要的支撑部件&#xff0c;其选择直接影响到设备的稳定性和使用寿命&#xff0c;尤其是在机床中&#xff0c;不同的机床对螺杆支撑座的要求也是不同的。 1、精度&#xff1a;精密测量用的基准平面和精密机床机械的检验测量设备&#xff0c;需要使用高精…

在Spring Boot中,可以通过实现一些特定的接口来拓展Starter

在Spring Boot中&#xff0c;开发者可以通过实现一些特定的接口来拓展Starter。这些接口允许开发者自定义Spring Boot应用程序的配置和行为&#xff0c;从而创建功能丰富且易于使用的Starter。以下是一些关键的接口&#xff0c;用于拓展Starter&#xff1a; EnvironmentPostPro…

深入理解 tree 命令行工具:目录结构可视化的利器

文章目录 前言1. 什么是 tree 命令&#xff1f;安装 tree 2. tree 的基本用法显示当前目录的树状结构显示指定目录的树状结构 3. tree 的常用选项3.1 显示隐藏文件3.2 排除特定目录或文件3.3 限制递归深度3.4 显示文件大小3.5 显示文件的权限信息3.6 将输出保存到文件 4. 实际应…

Federated learning client selection algorithm based on gradient similarity阅读

基于梯度相似性的联邦学习客户端选择算法 Abstract 摘要introduction**背景****目的****结论****结果****讨论****思路** 链接&#xff1a;https://link.springer.com/article/10.1007/s10586-024-04846-0 三区 Abstract 摘要 联邦学习&#xff08;FL&#xff09;是一种创新的…

【测试工具】如何使用 burp pro 自定义一个拦截器插件

在 Burp Suite 中&#xff0c;你可以使用 Burp Extender 编写自定义拦截器插件&#xff0c;以拦截并修改 HTTP 请求或响应。Burp Suite 支持 Java 和 Python (Jython) 作为扩展开发语言。以下是一个完整的流程&#xff0c;介绍如何创建一个 Burp 插件来拦截请求并进行自定义处理…

网络编程的概念&作用

网络编程是什么&#xff1f; 想象一下&#xff0c;你和朋友在不同的房间里&#xff0c;你们想互相传递纸条聊天。网络编程就像是编写一套规则&#xff0c;让计算机能够通过网络&#xff08;比如互联网&#xff09;互相传递信息。这些信息可以是文字、图片、视频&#xff0c;甚…

航天军工与金融行业 UE/UI 设计:跨越领域的体验革新之道

在数字化时代&#xff0c;用户体验&#xff08;UE&#xff09;和用户界面&#xff08;UI&#xff09;设计成为众多行业提升竞争力的关键因素。航天军工与金融行业虽业务性质差异巨大&#xff0c;但在 UE/UI 设计方面却面临着一些相似挑战&#xff0c;同时也在各自的探索中展现出…

【Git】--- 分支管理

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; Git 本篇博客我们来介绍Git的一个重要功能之一 ---- 分支。我们将讲解关于分支的各种操作&#xff0c;以及如何帮助我们进行开发。 &#x1f3e0; 理解分支…

纯血鸿蒙:中国操作系统自主创新的里程碑

引言&#xff1a;破局者登场 2024 年 10 月&#xff0c;搭载纯血鸿蒙操作系统&#xff08;HarmonyOS NEXT&#xff09;的华为 Mate 70 系列正式发布&#xff0c;首日预约量突破 330 万。这场现象级热度的背后&#xff0c;不仅是消费者对硬件创新的期待&#xff0c;更是中国科技…

二造考试的备考过程中如何保持良好的心态?

在二级造价师考试的备考过程中&#xff0c;保持良好的心态至关重要&#xff0c;以下是一些有效的方法&#xff1a; 树立正确的考试观念 )认识到二级造价师考试是职业生涯中的一个重要环节&#xff0c;但不是唯一的决定因素。把它看作是提升自己专业能力、丰富知识储备的机会&am…