当前位置: 首页 > news >正文

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

  

目录

一、数据结构和算法

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私信告诉我~~ 🥰🥰😉

相关文章:

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

目录 一、数据结构和算法 1.什么是数据结构&#xff1f; 2.什么是算法&#xff1f; 3.数据结构和算法的重要性 二、算法的时间复杂度和空间复杂度 1.算法效率 2.算法的复杂度 3.复杂度在校招中的考察 4.时间复杂度 5.空间复杂度 6.常见复杂度对比 7.复杂度的OJ练…...

HTTP API接口设计规范

1. 所有请求使用POST方法 使用post&#xff0c;相对于get的query string&#xff0c;可以支持复杂类型的请求参数。例如日常项目中碰到get请求参数为数组类型的情况。 便于对请求和响应统一做签名、加密、日志等处理 2. URL规则 URL中只能含有英文&#xff0c;使用英文单词或…...

数据一致性校验(pt-table-checksum)

介绍 pt-table-checksum 和 pt-table-sync 是 percona 公司发布的、检查 MySQL 主从数据库数据一致性校验的工具。pt-table-checksum 利用 MySQL 复制原理&#xff0c;在主库执行校验和计算&#xff0c;并对比主从库校验和&#xff0c;由此判断主从库数据是否一致。如果发现数…...

Talk预告 | 新加坡国立大学郑奘巍 AAAI‘23 杰出论文:大批量学习算法加速推荐系统训练

本期为TechBeat人工智能社区第486期线上Talk&#xff01; 北京时间3月30日(周四)20:00&#xff0c;新加坡国立大学二年级博士生——郑奘巍的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “大批量学习算法加速推荐系统训练”&#xff0c;届时将分…...

肖 sir_就业课__004项目流程(H模型)

项目流程&#xff1a; 一、面试提问&#xff08;h模型&#xff09; 1、你说下你们公司测试流程&#xff1f; 2、给你一个需求你会怎么做? 3、你讲下你的工作&#xff1f; 4、谈谈你是如何去测试&#xff1f; 答案&#xff1a;h模型 要求第一人称来写 讲解简化文字流程&#x…...

snipaste 截图工具——可以使图片悬浮在任何软件上,方便对比

一、下载 官网下载地址&#xff1a;Snipaste Downloads &#xff08;需要梯子&#xff09; CSDN下载地址&#xff1a;https://download.csdn.net/download/weixin_43042683/87671809 1. 下载 压缩包后&#xff0c;免安装&#xff0c;直接解压后既可以使用。 2. 点击Snipaste.…...

Docker 快速部署Springboot项目

编写Dockerfile文件 # Docker image for springboot file run # VERSION 0.0.1 # Author: # 基础镜像使用java FROM openjdk:8 # 作者 MAINTAINER laihx # VOLUME 指定了临时文件目录为/tmp。 # 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件&#xff0c;并链接到…...

【LeetCode: 剑指 Offer II 112. 最长递增路径 | 递归 | DFS | 深度优先遍历 | 记忆化缓存表】

&#x1f34e;作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域新星创作者&#x1f3c6;&#xff0c;保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享&#x1f48e;&#x1f48e;&#x1f48e; &#x1f34e;座右…...

hive 入门 一般用于正式环境 修改元数据(二)

安装配置可参考 https://blog.csdn.net/weixin_43205308/article/details/130020674 1、如果启动过derby&#xff0c;最小初始化过 在安装路径下删除 derby.log metastore_db rm -rf derby.log metastore_db此处省略安装mysql数据库 2、配置MySQL 登录mysql mysql -uroot …...

在RedHat系统上使用firewall-cmd命令可以将端口打开

在RedHat系统上使用firewall-cmd命令可以将端口打开&#xff0c;具体操作如下&#xff1a; 首先&#xff0c;检查当前系统使用的防火墙服务&#xff0c;比如firewalld或iptables&#xff0c;使用以下命令&#xff1a; systemctl status firewalld # 检查firewalld服务 system…...

分享(五):免费可用的多种类 API 大全集合整理

前言 搜罗了各大平台整理了一波免费可以用的 API &#xff0c;有需要的收藏起来啦。 实名认证 运营商二要素 API &#xff1a;运营商校验此姓名、手机号码是否一致。 运营商三要素 API&#xff1a;运营商验证姓名、身份证号码、手机号码是否一致&#xff0c;返回验证结果称…...

8.1 假设验证的基本概念

学习目标&#xff1a; 要学习假设检验的基本概念&#xff0c;我会按照以下步骤进行&#xff1a; 了解假设检验的基本概念&#xff1a;假设检验是一种统计推断方法&#xff0c;用于判断某个假设是否成立。一般来说&#xff0c;假设检验包括原假设和备择假设两个假设&#xff0c…...

C语言基础

为了学习数据结构&#xff0c;整理一篇基础的C语言入门知识&#xff08;仅供自身学习用&#xff09; 条件运算符 语法&#xff1a;exp1 ? exp2 : exp3; exp1是条件表达式&#xff0c;如果结果为真&#xff0c;返回exp2 如果结果为假&#xff0c;返回exp3 if (a > b)max …...

Docker教程:如何将Helix QAC创建为一个容器并运行?

在这个Docker教程中&#xff0c;你将了解到如何将Helix QAC创建为一个容器化的镜像并运行。 Docker的基本定义是一个开源且流行的操作系统级虚拟化&#xff08;通常称为“容器化”&#xff09;技术&#xff0c;它是轻量级且可移植的&#xff0c;主要在Linux和Windows上运行。D…...

1676_MIT 6.828 xv6中的CPU alarm_资料翻译整理

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 我觉得看了几个MIT的课程之后让我觉得我的大学四年有点浪费时光&#xff0c;看起来MIT的课程的确是很有饱满度。 这里&#xff0c;再整理一份课程中的作业要求。 …...

记一次内存泄漏问题的排查

阶段一&#xff1a; 前段时间&#xff0c;突然发现服务在毫无征兆的情况下发生了重启。去看了一下容器退出的日志&#xff0c;发现内存利用率超过了100%&#xff0c;导致容器重启&#xff0c;进一步看了skyWalking&#xff0c;发现heap内存超了&#xff0c;当时只是简单的以为是…...

QML控件--Drawer

文章目录一、控件基本信息二、控件使用三、属性成员一、控件基本信息 Import Statement&#xff1a;import QtQuick.Controls 2.14 Since&#xff1a;Qt 5.7 Inherits&#xff1a;Popup 二、控件使用 Drawer&#xff1a;提供一个可以使用滑动手势打开和关闭的侧面板&#xff…...

PHY- PHY芯片概述

1 PHY概述 关于Internet Protocal的分层模型可以参考文章 :【Internet Protocal-OSI模型中的网络分层模型】,下面我们讲讲底层以太网控制器和收发器的知识。其主要是处理OSI模型中的物理层和链路层的事情。 在CAN/CANFD、FlexRay等总线中,有控制器Controller和收发器Transc…...

【C++】如何获取当前正在运行的函数的名称?

func、FUNCTION、__PRETTY_FUNCTION__的区别 常用获取函数名成的方法都有__func__、FUNCTION、PRETTY_FUNCTION。那么它们的区别是什么呢&#xff1f;   1) func、FUNCTION&#xff1a; 主要是获取函数的名称。   2) PRETTY_FUNCTION&#xff1a; 不仅能获取函数的名称&am…...

42.原型对象 prototype

目录 1 面向对象与面向过程 2 原型对象 prototype 3 在内置对象中添加方法 4 constructor 属性 5 实例对象原型 __proto__ 6 原型继承 7 原型链与instanceof 7.1 原型链 7.2 instanceof 8 案例-模态框 1 面向对象与面向过程 编程思想有 面向过程 与 面向…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

[特殊字符] 手撸 Redis 互斥锁那些坑

&#x1f4d6; 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作&#xff0c;想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁&#xff0c;也顺便跟 Redisson 的 RLock 机制对比了下&#xff0c;记录一波&#xff0c;别踩我踩过…...

Easy Excel

Easy Excel 一、依赖引入二、基本使用1. 定义实体类&#xff08;导入/导出共用&#xff09;2. 写 Excel3. 读 Excel 三、常用注解说明&#xff08;完整列表&#xff09;四、进阶&#xff1a;自定义转换器&#xff08;Converter&#xff09; 其它自定义转换器没生效 Easy Excel在…...