深入理解数据结构(1):复杂度详解

news2024/11/26 18:35:53

标头风景图片


  • 文章主题:复杂度详解🌱
  • 所属专栏:深入理解数据结构📘
  • 作者简介:更新有关深入理解数据结构知识的博主一枚,记录分享自己对数据结构的深入解读。😄
  • 个人主页:[₽]的个人主页🔥🔥

复杂度详解

  • 前言
  • 算法效率
    • 如何分析一个算法的好坏
    • 算法的复杂度
    • 复杂度在校招中的考察
  • 时间复杂度
    • 时间复杂度的概念
    • 大O的渐进表示法
    • 常见时间复杂度计算举例
  • 空间复杂度
  • 常见复杂度对比
  • 结语

前言

复杂度是计算机领域表示所需资源达到某个大小量级的量度,源自计算复杂性理论1
主要分为时间复杂度与空间复杂度等,通常复杂度会被用来综合分析一个算法的好坏,以下是我对复杂度的详细解释。


算法效率

如何分析一个算法的好坏

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

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

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

算法的复杂度

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

复杂度在校招中的考察

校招中的复杂度考察


时间复杂度

时间复杂度的概念

时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语句的执行次数成正比,虽然每个语句的执行时间不同,相同语句每次的执行时间也不同,但是每次的差距不会很大,语句的执行次数的多少能够大体地反映出一个程序所需要耗费的时间量级(该算法所需的时间在多大的一个时间量级中,一个时间量级能够反映算法运行的一个大概大小的时间区间),算法中的基本操作的执行次数,为算法的时间复杂度
:找到一个算法中基本语句出现次数与问题规模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 F(N) = N^2 + 2*N + 10 F(N)=N2+2N+10

  • N = 10 F(N) = 130
  • N = 100 F(N) = 10210
  • N = 1000 F(N) = 1002010
    实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法

大O的渐进表示法

大O符号(Big O notation):是用于描述函数渐进行为的数学符号
推导大O阶方法
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项(比一定狭隘地指谁的次方项最高,因为完全有可能项不是单纯的次方项的初等函数不能化简的表达形式,最高阶项可直接简单地理解成是画函数图象时,谁在正半轴上图象大部分时候比另一函数大,谁就是复杂度大O的表示法当中的“最高次项”,这里的最高次项是一种更加广义的、自由的、开阔的、凭一点感觉的、稍稍没有那么自由的表示方法,这里的命名方法是根据初等函数的种类,以及幂函数的次方项的多少来命名的(这几种情况出现频率最高),当然如果对数的底数不同,以及真数的表达式不同也能够用来命名,但一般这种情况出现的频率少,就不做讨论了,阶的分类根据表达式的初等函数的类型不同以及各初等函数的数据不同是可以分成很多种情况的,一般复杂度的化简规则是都可以又能力将表达式化成一个单一的初等函数的形式的,一般复杂度化简后表达式都会变成单一的一项(不同项同阶的会被合并(初等函数中的某一类型的两项各数据完全相同才叫同阶,不同阶就按上述的简单万能判别方法,谁在上多一些,对整个表达式的结果影响大一些,就会去选择两项或多项中的在输入规模的范围(即各函数的定义域)中居于上部最多分布的那一项去表达(不同的输入规模的范围比较相同的几个不同阶的函数也可能会得到不同结果的最高阶项函数)))乘积形式时就会是两个初等函数相乘,约不掉)。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
使用大O的渐进表示法以后,Func1的时间复杂度为:

O ( N 2 ) O(N^2) O(N2)

  • N = 10 F(N) = 100
  • N = 100 F(N) = 10000
  • N = 1000 F(N) = 1000000
    通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了地表示出了执行次数

4、另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
例如:在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到
在实际中一般情况关注的是算法的最坏运行情况,大O渐进表示法是为确保其准确性表达方式是考虑最坏情况的严谨表示法,所以数组中搜索数据时间复杂度为O(N)

常见时间复杂度计算举例

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

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

实例3

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

实例4

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

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

实例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;
 else
 return mid;
 }
 
 return -1;
}

实例7

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

实例8

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

实例答案及分析

  1. 实例1基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N)
  2. 实例2基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M)
  3. 实例3基本操作执行了10次,通过推导大O阶方法,时间复杂度为 O(1)
  4. 实例4基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为O(N)
  5. 实例5基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N2)
  6. 实例6基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(logN)
    ps:logN在算法分析中表示是底数任意的N的对数,有些地方会写成lgN(数学中的lgN单纯只指底数为2的N的对数,但是在时间复杂度中其代表底数任意的N的对数的logN的缩写形式的缩写,本质是因为计算机中不好写出底数,且时间复杂度本就是估算的形式,所以就可以直接将任意底数的只有一个任意输入规模的输入规模表达式的对数形式全都缩写成logF(N)的形式,方便表达的同时也符合了大O的渐进表达法表达时间复杂度的去掉影响不大的项,简洁明了地表示出执行次数(估算)的特点)。
  7. 实例7通过计算分析发现基本操作递归了N次,时间复杂度为O(N)。
  8. 实例8通过计算分析发现基本操作递归了2N次,时间复杂度为O(2N)。

空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度
空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟时间复杂度类似,一般也直接使用大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;
 }
}

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

实例3

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

实例答案及分析

  1. 实例1使用了常数个额外空间,所以空间复杂度为 O(1)
  2. 实例2动态开辟了N个空间,空间复杂度为 O(N)
  3. 实例3递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N)

常见复杂度对比

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

真实表达式大O的渐进表示法
5201314 5201314 5201314 O ( 1 ) O(1) O(1)常数阶
3 n + 4 3n+4 3n+4 O ( n ) O(n) O(n)线性阶
3 n + 4 n + 5 3^n + 4n + 5 3n+4n+5 O ( n 2 ) O(n^2) O(n2)平方阶
3 l o g 2 n + 4 3log_2n + 4 3log2n+4 O ( l o g n ) O(logn) O(logn)对数阶
2 n + 3 n l o g 2 n + 14 2n + 3nlog_2n + 14 2n+3nlog2n+14 O ( n l o g n ) O(nlogn) O(nlogn)nlogn阶
n 3 + 2 n 2 + 4 n + 6 n^3 + 2n^2 + 4n + 6 n3+2n2+4n+6 O ( n 3 ) O(n^3) O(n3)立方阶
2 n 2^n 2n O ( 2 n ) O(2^n) O(2n)指数阶

大O表达法的各阶函数在0~+无穷时的函数图象


结语

以上就是博主对复杂度的详解,😄希望对你的数据结构的学习有所帮助!看都看到这了,点个小小的赞或者关注一下吧(当然三连也可以~),你的支持就是博主更新最大的动力!让我们一起成长,共同进步!


  1. 计算复杂性理论(Computational complexity theory)是理论计算机科学和数学的一个分支,它致力于将可计算问题根据它们本身的复杂性分类,以及将这些类别联系起来。一个可计算问题被认为是一个原则上可以用计算机解决的问题,亦即这个问题可以用一系列机械的数学步骤解决,例如算法。 ↩︎

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

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

相关文章

[C++]使用OpenCV去除面积较小的连通域

这是后期补充的部分&#xff0c;和前期的代码不太一样 效果图 源代码 //测试 void CCutImageVS2013Dlg::OnBnClickedTestButton1() {vector<vector<Point> > contours; //轮廓数组vector<Point2d> centers; //轮廓质心坐标 vector<vector<Point&…

概率、似然、极大似然估计

概率、似然、极大似然估计 概率&#xff1a;特定情况下某事件发生的可能性&#xff08;参数已知&#xff0c;事件发生的可能性&#xff09;似然&#xff1a;根据已经确定的结果推测产生这个结果的可能的环境&#xff08;事件发生的可能性已知&#xff0c;参数未知&#xff0c;推…

C++心决之内联函数+auto关键字+指针空值

目录 7.内联函数 7.1 概念 7.2 特性 8. auto关键字(C11) 8.1 类型别名思考 8.2 auto简介 8.3 auto的使用细则 8.4 auto不能推导的场景 9. 基于范围的for循环(C11) 9.1 范围for的语法 9.2 范围for的使用条件 10. 指针空值nullptr(C11) 10.1 C98中的指针空值 7.内联…

计算机专业学生有必要参加软考吗?

有&#xff0c;因为后面真的用得上。 一、行业背景&#xff1a; 你考虑过毕业以后从事什么工作吗&#xff1f;大概率会是去做IT吧&#xff0c;随着这几年总体就业形势不太好&#xff0c;给各位计算机专业毕业生跨行择业的机会也越来越少&#xff0c;再加之外有强敌虎视眈眈&am…

c#仿ppt案例

画曲线 namespace ppt2024 {public partial class Form1 : Form{public Form1(){InitializeComponent();}//存放所有点的位置信息List<Point> lstPosition new List<Point>();//控制开始画的时机bool isDrawing false;//鼠标点击开始画private void Form1_MouseD…

蓝牙耳机推荐哪个品牌好?2024火爆机型推荐,拒绝云测

​音乐和有声读物是许多人放松身心、缓解等待无聊时刻的好伴侣。尽管市面上蓝牙耳机琳琅满目&#xff0c;挑选合适的款式却颇具挑战。作为一个经验丰富的耳机用户&#xff0c;我深知哪些蓝牙耳机值得你的信赖。接下来&#xff0c;我将分享几款我个人认为很不错的蓝牙耳机来给大…

hcia datacom课程学习(5):MAC地址与arp协议

1.MAC地址 1.1 含义与作用 &#xff08;1&#xff09;含义&#xff1a; mac地址也称物理地址&#xff0c;是网卡设备在数据链路层的地址&#xff0c;全世界每一块网卡的mac地址都是唯一的&#xff0c;出厂时烧录在网卡上不可更改 &#xff08;2&#xff09;作用&#xff1a…

Git 如何合并多个连续的提交

我平常的编程喜欢是写一段代码就提交一次&#xff0c;本地一般不攒代码&#xff0c;生怕本地有什么闪失导致白干。但这样就又导致一个问题&#xff1a;查看历史日志时十分不方便&#xff0c;随便找一段提交可以看到&#xff1a; > git log --oneline 8f06be5 add 12/qemu-h…

突破数据障碍—如何使用IP代理服务获取量子科学研究领域最新数据

写在前面 在这个数字化的时代&#xff0c;人们越来越关注隐私保护和网络访问自由。我最近也深入研究了一下IP代理服务&#xff0c;在规避地理限制、绕过封锁以及保护个人隐私方面&#xff0c;它确实发挥了关键作用。 一、基础介绍 起因是有个项目需要对量子领域进行深入的研究之…

Android Studio学习5——布局layout与视图view

wrap_content&#xff0c;内容有多大&#xff0c;就有多宽&#xff08;包裹&#xff09; 布局 padding 边框与它自身的内容 margin 控件与控件之间

漫谈测试策略

作者&#xff1a;小瑕 一、测试策略是什么&#xff1f; 策略&#xff1a; 基本含义&#xff1a;指为达到某种目的而制定的行动方案或计划。 详细解释&#xff1a;策略是指在特定情况下为达到某种目的而采取的有系统性的行动方案或计划。它通常包括分析、决策和执行三个阶段。策…

gitlab代码迁移,包含历史提交记录、标签、分支

1、克隆现有的GitLab仓库&#xff08;http://localhost:8888/aa/bb/cc.git&#xff09;到本地&#xff0c;包括所有分支和标签 git clone --bare http://localhost:8888/aa/bb/cc.git 2、在gitlab上创建一个空的仓库&#xff08;http://localhost:7777/aa/bb/cc.git&#xff…

浅谈通信校验码及 CRC 校验

一、信息论中的 CRC 我上大学的时候,有一门课程叫做信息论,我就是从这个课程中学到的 CRC 校验这个词的,没错,当时学完整个课程后,CRC 对我来说依然只是一个单薄的缩写词语,全称我都不知道是啥。 CRC 全称是循环冗余校验(Cyclic Redundancy Check)。 说到信息论中的…

vue3+ts 调用接口,数据显示

数据展示 &#xff08;例&#xff1a;展示医院等级数据&#xff0c;展示医院区域数据同理。&#xff09; 接口文档中&#xff0c;输入参数 测试一下接口&#xff0c;发请求 看是否能够拿到信息 获取接口&#xff0c;api/index.ts 中 /home/index.ts // 统一管理首页模块接口 i…

SPI通信----Flash存储器W25Q64

SPI怎么配置&#xff1a; 控制器配置&#xff08;更稳定效率高&#xff09; IO模拟的方式&#xff08;更灵活移植性高&#xff09; 首先我会选择用IO模拟的方式配置&#xff0c;SPI根据时钟极性以及时钟相位的不同对应不同等的时序&#xff0c;我选择用时钟极性和时钟相位都…

The Google File System [SOSP‘03] 论文阅读笔记

原论文&#xff1a;The Google File System 1. Introduction 组件故障是常态而非例外 因此&#xff0c;我们需要持续监控、错误检测、容错和自动恢复&#xff01; 按照传统标准&#xff0c;文件数量巨大大多数文件都是通过添加新数据而不是覆盖现有数据来改变的&#xff0c;因…

基于springboot实现教师人事档案管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现在线商城系统演示 摘要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本ONLY在线商城系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理…

GD32F470 GY-906 MLX90614ESF BAA BCC DCI IR红外测温传感器模块温度采集模块移植

2.12 MLX90614红外无接触测温传感器 MLX90614 系列模块是一组通用的红外测温模块。在出厂前该模块已进行校验及线性化&#xff0c;具有非接触、体积小、精度高&#xff0c;成本低等优点。被测目标温度和环境温度能通过单通道输出&#xff0c;并有两种输出接口&#xff0c;适合于…

齐力控股集团现已加入2024年第13届生物发酵展

参展企业介绍 齐力控股集团专业生产高精度卫生级不锈钢设备配件及管道所有连接件、锻造、精加工一站式服务。产品广泛适用于制药、饮料、乳制品、啤酒、生物化工等领域。所有产品均按3A、SMS、DIN、RJT、IDF、DS等标准制造&#xff0c;所有产品均达到GMP药典要求。我们是一家有…

数字乡村创新之路:科技引领农村实现高质量发展

随着信息技术的快速发展&#xff0c;数字乡村建设已成为推动农村高质量发展的重要引擎。数字乡村通过科技创新&#xff0c;不仅改变了传统农业生产方式&#xff0c;也提升了乡村治理水平&#xff0c;为农民带来了更加便捷的生活。本文将从数字乡村的内涵、科技引领农村高质量发…