【数据结构与算法篇】时间复杂度与空间复杂度

news/2024/5/18 19:54:15/文章来源:https://blog.csdn.net/qq_63320529/article/details/130043532

  

目录

一、数据结构和算法

1.什么是数据结构? 

2.什么是算法?

3.数据结构和算法的重要性

二、算法的时间复杂度和空间复杂度

1.算法效率

2.算法的复杂度

3.复杂度在校招中的考察

4.时间复杂度

5.空间复杂度 

6.常见复杂度对比

7.复杂度的OJ练习


 

👻内容专栏:《数据结构与算法》

🐨本文概括: 讲解数据结构和算法的概念、时间复杂度、空间复杂度、常见复杂度对比。

🐼本文作者:花 碟

🐸发布时间:2023.4.13

一、数据结构和算法

1.什么是数据结构? 

数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。 就是说方便在内存中管理数据,进行增删查改的操作。

2.什么是算法?

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

3.数据结构和算法的重要性

目前校园招聘笔试一般采用Online Judge形式, 一般都是20-30道选择题+2道编程题,或者3-4道编程题。 

 

 

可以看出,现在公司对学生代码能力的要求是越来越高了,大厂笔试中几乎全是算法题而且难度大,中小长的笔试中才会有算法题。算法不仅笔试中考察,面试中面试官基本都会让现场写代码。而算法能力短期内无法快速提高了,至少需要持续半年以上算法训练积累,否则真正校招时笔试会很艰难,因此算法要早早准备。

数据结构与算法对一个程序员来说的重要性? 👈这篇文章是知乎一篇博主对于数据结构与算法的详细介绍,感兴趣的小伙伴们可以看看。

二、算法的时间复杂度和空间复杂度

1.算法效率

如何衡量一个算法的好坏呢?比如对于以下斐波那契数列

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

斐波那契数列的递归实现方式非常简洁,但简洁一定好吗?那该如何衡量其好与坏呢?

2.算法的复杂度

算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度
时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度

3.复杂度在校招中的考察

4.时间复杂度

👇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执行的基本操作次数是多少?

👉计算Func1执行的基本操作次数(函数表达式): F(N) = N² + 2 * N + 10
我们接下来给予一些值进行计算F(N)与N的关系
当N = 10  F(N) = 130

当N = 100  F(N) = 10210

当N = 1000  F(N) = 1002010

当N = 10000 F(N) = 100020010

结论:我们发现随着N的增大,2*N + 10的结果对F(N)的整体结果影响会越来越小,其主要影响的一项是

所以,实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要估算大概执行次,计算出一个量级就行。那么这里我们使用大O的渐进表示法

👇2.大O的渐近表示法

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

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

👇3.常见时间复杂度计算举例

🎀实例1:

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

实例1基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N)

🎀实例2:

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

实例2基本操作执行了M+N次,有两个未知数M和N,无法确定M是否远大于(小于)N,或者说两者接近,所以时间复杂度为 O(M+N)

🎀实例3:

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

实例3基本操作执行了100次,通过推导大O阶方法,时间复杂度为 O(1) 

注⚠️:O(1)不是1次,指的是常数次。

 🎀实例4:

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

实例4 在字符串中寻找字符。好的情况在第1次就找到了,最坏的情况在尾部找到。基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为 O(N)

 🎀实例5:

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

实例5基本操作执行最好N次,即第一趟进去查找已经是有序的数字了,最好情况是O(N)最坏的情况呢,一共需要N - 1趟嘛,从N - 1 趟开始,执行N - 1次、N - 2、N - 3 …… 3、2 、1,可以看出来,这是一个等差数列求和,最坏执行了N*(N-1)/2, 通过推导大O阶方法+时间复杂度一般看最坏,故时间复杂度为 O(N^2)

 🎀实例6:

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

实例6 基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(log₂N)。ps:logN在算法分析中表示是底数为2,对数为N。有些地方会写成lgN

 🎀实例7:

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

实例7 通过计算分析发现基本操作递归了N次,每次调用执行常数次,所以时间复杂度为O(N) 

 🎀实例8:

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

 画图分析:

实例8 斐波那契数列每一项都是递归调用两个子项我们可以将斐波那契数列的调用过程大致用画图画出来,函数往后调用次数会越来越多,但是到最后几次快调用完了的时候,右边的部分会提前加结束,调用次数会大幅度减少,呈现一个三角形(灰色部分为数据缺失部分),画图不够准确,但大体思想可以这么表示。我们发现,其中调用的每一层是2的倍数,可以看作是一个等比数列,运用错位相减的方法,求出基本操作递归了2^(N - 1) - 1次,故时间复杂度为O(2^N)

5.空间复杂度 

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

空间复杂度计算举例 

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

实例1 使用了常数个额外空间,所以空间复杂度为 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;
}

实例2动态开辟了N个空间,空间复杂度为 O(N)

🎏实例3: 

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

实例3递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为 O(N) 

6.常见复杂度对比

一般算法常见的复杂度如下:

 

7.复杂度的OJ练习

1.消失的数字 17.04 消失的数字_点击链接跳转

2.轮转数组  189.轮转数组_点击链接跳转

 


🤗🤗 好啦,本篇文章就到此为止啦~ 感谢大家的支持!希望对你有帮助,如有什么疑问,可以在评论区or私信告诉我~~ 🥰🥰😉

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

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

相关文章

射频功率放大器在液体超声声强的光电测量中的应用

实验名称&#xff1a;液体中超声声强的光电测量 研究方向&#xff1a;光电测量 测试目的&#xff1a; 声强是描述声场的基本物理量口&#xff0c;超声效应直接与声强有关。例如在工程技术领域&#xff0c;液体中的声场分布直接影响流场分布口&#xff0c;声强的大小影响着超声波…

ChatGPT 学习 ES lucene 底层写入原理,源码

一直有个疑问“学习最新版lucene 数据写入相关的源码&#xff0c;应该看哪些源码&#xff0c;以什么顺序看&#xff08;先看什么&#xff0c;后看什么&#xff09;&#xff1f;” 对于Lucene的数据写入过程&#xff0c;可以分为以下几个阶段 在学习Lucene的数据写入相关的源码…

Nextcloud去掉URL中的index.php以及强制https(Win10子系统WSL)

一、Nextcloud去掉URL中的index.php 1、启用相关模块 cd /var/www/nextcloud #进入程序目录sudo chmod -R 777 .htaccess #设置.htaccess文件权限可读写sudo a2enmod envaudo a2enmod rewrite #启用rewrite模块2、修改nextcloud配置文件 vim /var/www/nextcloud/config/…

<数据结构>NO1.算法的时空复杂度

文章目录&#x1f6a9;算法效率算法复杂度&#x1fa85;时间复杂度大O的渐进表示法常见的时间复杂度举例&#x1fa85;空间复杂度大O的渐进表示法常见的空间复杂度举例&#x1f5ef;️常见复杂度对比&#x1f5ef;️&#x1f6a9;算法效率 算法是一个被设计好的&#xff0c;计…

CentOS7.6 磁盘挂载

CentOS7.6 磁盘挂载 目录CentOS7.6 磁盘挂载1.磁盘说明2.磁盘分区步骤1.磁盘说明 1、Linux硬盘分IDE硬盘和SCSI硬盘&#xff0c;目前基本上是SCSI硬盘 2、对于IDE硬盘&#xff0c;驱动器标识符为"hdx"&#xff0c;""代表分区&#xff0c;前四个分区用数字…

停车场管理系统文件录入(C++版)

❤️作者主页&#xff1a;微凉秋意 ✅作者简介&#xff1a;后端领域优质创作者&#x1f3c6;&#xff0c;CSDN内容合伙人&#x1f3c6;&#xff0c;阿里云专家博主&#x1f3c6; 文章目录一、案例需求描述1.1、汽车信息模块1.2、普通用户模块1.3、管理员用户模块二、案例分析三…

C++中的引用变量详解

文章目录声明及定义代码引用变量的特点图片解释引用变量的本质引用变量的用途int & 和 const int & 的区别引用变量和宏定义&#xff08;#define&#xff09;的区别声明及定义 [const] int& 变量名 右值 注意&#xff1a;[]内的是可选的。即这里的const限定词是可…

香港布局Web3.0 既是金融试探,也是未来战略

香港Web3.0协会成立的消息已在业内刷屏&#xff0c;作为跨业界的非盈利机构&#xff0c;该协会致力于促进Web3.0生态环境的建设&#xff0c;港府特首李家超和北京中央驻港联络办公室部分领导均出席了成立典礼。 李家超在致辞中表示&#xff0c;Web3.0的发展正值黄金起点&#x…

SpringCloud GateWay与Nacos使用

网关就相当于一个内网与外网的出入口&#xff0c;起着 安全、验证的功能&#xff0c;如果没有网关&#xff0c;那么如果需要实现验证的功能&#xff0c;除非 SpringCloud GateWay 作为微服务的网关,起着如下作用 ① 作为所有API接口服务请求的接入点 ② 作为所有后端业务服务…

如何在企业微信中使用低代码工具?

企业微信是一款非常强大的办公应用软件&#xff0c;可以方便地进行企业内部的沟通、协作、管理等工作。虽然企业微信本身并不提供低代码工具&#xff0c;但是可以通过集成第三方的低代码工具来实现在企业微信中的使用。 例如&#xff0c;可以使用低代码平台简道云&#xff0c;…

蓝桥杯之我见

前言 关于蓝桥杯&#xff0c;应该有很多人不知道这是一个什么样的比赛。但是作为一名合格的程序员&#xff0c;就算之前没有参加过蓝桥杯的比赛&#xff0c;或者没听说过蓝桥杯&#xff0c;读完本篇文章再说不知道蓝桥杯&#xff0c;就有点不合适了吧&#xff1f;&#xff01;那…

Linux驱动之LED驱动

之前学习完了字符设备驱动的大体框架&#xff0c;现在我们就使用这个基本的框架来对硬件进行操作&#xff0c;例如通过指令控制led的状态&#xff0c;编写LED驱动。LED驱动有多种实现方式。 目录 GPIO函数 IO内存映射 混杂设备驱动 GPIO函数 首先加入需要的头文件。 #incl…

为一副通用纸牌设计数据结构

为一副通用纸牌设计数据结构 大家好&#xff0c;我是易安&#xff0c;今天我们来聊一道笔试题&#xff0c;这也是我曾经面试华为时做过的题&#xff0c;今天分享给大家。 题目&#xff1a; 如何设计一个通用的扑克牌数据结构&#xff1f;请解释如何继承它来实现特定的扑克游戏…

如何将本地项目上传到Github的方法步骤

默认&#xff1a;已经安装好了git。 第一步&#xff1a;我们需要先创建一个本地的版本库&#xff08;其实也就是一个文件夹&#xff09;。 你可以直接右击新建文件夹&#xff0c;也可以右击打开Git bash命令行窗口通过命令来创建。 第二步&#xff1a;通过命令git init把这个…

【C++】1. 命名空间

文章目录一、命名空间的由来二、命名空间的使用2.1 关键字&#xff1a;namespace2.2 访问命名空间里的标识符2.3 命名空间的特点三、总结一、命名空间的由来 当我们使用c语言编写项目时&#xff0c;可能遇到以下情况&#xff1a; 变量名与某个库函数名重复&#xff0c;导致保…

HTML学习(5)Canvas绘图

文章目录HTML5 CanvasHTML5 内联SVGHTML5 Canvas 使用 Canvas 进行绘图工作&#xff0c;Canvas元素用于在网页上绘制图片。 创建一个Canvas的元素&#xff1a; <canvas id"myCanvas" width"200" height"100"></canvas>但是Canvas…

部署zabbix代理服务器和snmp监控

目录 zabbix代理服务器 分布式监控的作用 部署zabbix代理服务器 在 Web 页面配置 agent 代理 snmp监控 SNMP简介 部署zabbix-snmp 服务端安装snmp监控程序 在 Web 页面配置 snmp 方式监控 zabbix代理服务器 分布式监控的作用 分担 server 的集中式压力 解决多机房之…

算法训练第五十五天 | 392.判断子序列、115.不同的子序列

动态规划part15392.判断子序列题目描述思路总结115.不同的子序列题目描述思路392.判断子序列 题目链接&#xff1a;392.判断子序列 参考&#xff1a;https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html 题目描述 给定字符串 s 和 t &…

小红书内容种草,曝光渠道分析总结

这是一个内容为王的时代&#xff0c;也是一个内容爆炸的时代。想要在以分享特色的小红书平台&#xff0c;实现内容种草&#xff0c;迅速出圈。今天来马文化传媒就从实操的角度&#xff0c;为大家带来小红书内容种草&#xff0c;曝光渠道分析总结的各种干货&#xff01; 一、什…

【C++】深度剖析string类的底层结构及其模拟实现

文章目录前言1. string的结构2. 构造、析构2.1 无参构造2.2 带参构造2.3 问题发现及修改c_stroperator []析构2.4 合二为一 ——全缺省3. 拷贝构造3.1 浅拷贝的默认拷贝构造3.2 深拷贝拷贝构造的实现4. 赋值重载4.1 浅拷贝的默认赋值重载4.2 深拷贝赋值重载的实现5. string对象…