手把手教你拿下时间空间复杂度,(包教包会)【上】

2023-11-20 19:51

本文主要是介绍手把手教你拿下时间空间复杂度,(包教包会)【上】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

手把手教你拿下时间复杂度,(包教包会)

  • 前言
  • 一、复杂度的意义
  • 二、时间复杂度的概念
  • 三、计算时间复杂度
  • 四、例题
    • 情况一
    • 情况二
    • 情况三
    • 情况四
    • 情况五
  • 结语

前言


这里是双非程序猿为大家带来的小技术分享,欢迎来到数据结构第一站——时间复杂度,这篇文章将会以多个例子讲解,并手把手教你教学,包教包会。
在这里插入图片描述

初学者刚学完c语言基本语法来学习数据结构的时候、或者后期学习的时候,普遍会将直接将时间复杂度与空间复杂度的概念忽略的(没错,就是我!😂)。
因为毕竟他和我们的编程似乎是没有什么直接的联系的。这样学习是不长远的,因为学到后面你会发现,你始终绕不开这两个复杂度的。
以严蔚敏老师的《数据结构与算法》这本教材来看的话,你每学完一个结构,教材一定会带你分析一遍时间复杂度与空间复杂度。由此可见其重要性。


来吧一起加油,拿下时间复杂度!
在这里插入图片描述

一、复杂度的意义

(如果觉得这段有点啰嗦的话,就直接看下一点吧)

算法效率分为两种:时间效率、空间效率。 时间效率被称为时间复杂度,空间效率被称为空间复杂度。时间复杂度主要衡量的是一个算法的运行速度,空间复杂度衡量的是一个算法所需的额外空间。

在计算机发展的初期,计算机的内存空间很小,所以我们十分看中算法的空间复杂度。但经过计算机行业的迅速发展,计算机的空间已经越来越大了,空间也就越来越不值钱了,因此我们现在更看中的是时间复杂度。

二、时间复杂度的概念

时间复杂度的定义为:在计算机科学中,时间复杂性,又称时间复杂度,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。
那我们要研究一个程序的时间复杂度是不是直接将程序上机跑一遍,记录他运行的时间就行了呢?这样是不行的,因为不同的电脑所带着的硬件不同,这会直接影响到程序的运行时间,因此这种方法不具有普遍性。

我们在估量一个算法的时间复杂度的方法其实很简单,计算它的执行次数。

举个例子

#indlude<stdio.h>
int main()
{printf("Hollow World!");return 0;
}

这里面的printf(“Hollow World!”); 与return 0;就相当于这个程序执行了两次。而两次就是这个程序的时间复杂度了吗?卖个小关子(千万别不往下看了哈😆,马上就有干货了)。

三、计算时间复杂度

先用一个例子来算一下

void Fun(int N)//该函数没有任何意义,是方便理解写的
{for(int i=0;i<N;i++)//嵌套循环在这里执行了N*N次。forint j=0;j<N;j++)cons++;//这是全局变量。for(int i=0;i<2*N;i++)//在这里执行了2*N次。cons++;for(int i=0;i<10;i++)//在这里执行了10次。cons++;
}

为了更好的展示主要执行次数,这个函数我们可以粗略认为它的时间复杂度的式子O=N²+2N+10次(为什么可以粗略认为会在下文解释,现在先将就一下哈哈),在这里我们只统计了主要的执行次数。然而时间复杂度其实是一种估算,是由影响时间复杂度最大项决定的,在这里影响最大的项为N²。原因如下:

N的值N²的值O的值
1113
10100130
1001000010210
100010000001002010

随N值的变化对时间复杂度影响最大因数其实是N²,所以时间复杂度为O(N²)。
时间复杂度的标准表示法为O(),()里面装的就是式子中最高阶。

大O符号:适用于描述函数中渐进行为的表示法。(实在理解不了的话也没关系)

干货,推导大O阶的方法方法:

1. 用常数1代替式中所有常数。
2. 在修改过的式子中保留最高阶。
3. 若最高阶为常数结果就是1。
4. 若最高阶不是常数的话结果就是这个最高阶。

接下来就是计算时间复杂度的几种情况。
在这里插入图片描述

四、例题

毫不夸张的说,接下来几个例子可以带你拿下时间复杂度

情况一

void Fun(int N)
{int count=0;for(int K=0;K<2*N;k++)//执行2N次count++;for(int M=10;M>0;M--)//执行10次count++;
}

在这里我们得出一个式子O=2N+10;

  • 用常数1代替式中所有常数。—— O=N+1。
  • 在修改过的式子中保留最高阶 ——O=N。
  • 得出结果O(N)。

情况二

那么这一段的时间复杂度呢?这一段就有点不一样了,不说别人,反正我是蒙在鼓里很久才知道是这样算的 😂。

void Fun2(int N,int M)
{int count=0;for(int k=0;k<N;k++)//执行次数为Nconst++;for(int k=0;k<M;k++)//执行次数为Mconst++;
}

我们得出一个式子:O=N+M,跟我一起算。

  • 用常数1代替式中所有常数。—— O=N+M。
  • 在修改过的式子中保留最高阶。——O=N+M。
  • 最高阶不为常数—— O(N+M)。

一般情况下时间复杂度为O(N+M),除非有特殊说明。
(1).当N远大于M时,时间复杂度为O(N)。
(2).当M远大于N时,时间复杂度为O(M)。
(3).当M与N差不多大的时候,O=2M,常数替换为1,保留最高阶——O(N)或 O(M)。

是不是干货满满,😎,关注我持续输出高质量博客。

情况三

void Fun3(int N)//这里的N对我们的时间复杂度没有影响
{int count=0;for(int k=0;k<100;k++)//在此处执行了100次count++;
}

我们得出一个式子:O=100.

  • 用常数1代替式中所有常数。—— O=1。
  • 保留最高阶。—— O=1。
  • 最高阶为常数,得出结果O(1)。

注意这里的1不是只执行1次的意思,而是最高阶为常数的意思。

情况四

这是一段在一个字符串里找字符的代码

int Fun4(char s,char a[N])//N为已知数
{int i;for(i=0;i<N;i++)if(a[i]==s)break;return i;
}

这里的时间复杂度是多少呢?和上面不同的是,在这里我的执行次数可以是1,可以是5,,也可以是N。那这样我们的时间复杂度到底是多少呢?先上干货:

  • 有些算法的时间复杂度存在最好、平均和最坏的情况
  • 最坏情况:操作执行的最大次数(上界)。
  • 平均情况:操作执行的期望运行次数。
  • 最好情况:操作执行的最小次数。

在这里查找次数的
最好情况为,1次。
平均情况为,N/2次。
最坏情况为,N次。

在这里时间复杂度应该取最坏情况O(N),原因是:虽然这样选不会那么精确,但绝对不会错

情况五

void Fun5(int a[N])//冒泡排序
{int temp;for(int end=N;end>0;end--){for(int j=0;j<end-1;j++){if(a[j]>a[j+1]){temp=a[j];a[j]=a[j+1];a[j+1]=temp;}}}
}

很多朋友看到有两个循环嵌套,应该就会想当然的得出时间复杂度就是O(N²),答案确实没错,但你真的知道这是怎么得出来的吗?而且并不是每个循环嵌套的结构时间复杂度都是O(N²)。

冒泡排序的思想就是把最大(或最小)的数不断后面或前面排。为了加深理解,我专门找了一张动态图:
在这里插入图片描述

了解了冒泡排序的同学不难想出,这个冒泡排序比较次数=N-1+N-2+N-3+N-4…+2+1=[(N+1)*N]/2(等差数列求和公式)。得出式子O=[(N+1)*N]/2。

  1. 常数变为1——O=(N+1)*N。
  2. 保留最高阶——O=N²。
  3. 最高阶不为常数,得出结果O(N²)。

不了解冒泡排序的同学可以私信我,包教包会。这么有诚意的博主就关注一个叭。

这五个例子一定要亲手算一遍,加深印象。相信在算完这五个例子后,程序猿们已经知道如何计算时间复杂度了。不过还有几个没这么常用的情况,我直接放在下面了,顺便做个比较。

O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(nⁿ)

抱歉在这里才解释,在上文我们之所以可以粗略的得出一个个式子的原因是,我们省略的执行次数是类似于“int count;”这样的操作,而这步操作的次数为1,对我们的时间复杂度O()不会产生影响
在这里插入图片描述

结语

我是大一新生一个,计算机小菜鸡一个。显然与许多大神不同的是,我的码龄还仅仅不到一年,现在尚处于一个知识的原始积累阶段。
但正因为我有一个和许多小白一样的水平,能使我更明白小白们的一些疑难杂症,从而对症下药。希望与大家一同进步。大学生博主,可以点点关注支持一下吗,或者免费的赞也可以鼓励鼓励我的哦,我会继续推出高质量博客,写的有问题的话,欢迎在评论区喷我😊。

在这里插入图片描述

这篇关于手把手教你拿下时间空间复杂度,(包教包会)【上】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/397044

相关文章

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

从基础到进阶详解Pandas时间数据处理指南

《从基础到进阶详解Pandas时间数据处理指南》Pandas构建了完整的时间数据处理生态,核心由四个基础类构成,Timestamp,DatetimeIndex,Period和Timedelta,下面我... 目录1. 时间数据类型与基础操作1.1 核心时间对象体系1.2 时间数据生成技巧2. 时间索引与数据

MySQL启动报错:InnoDB表空间丢失问题及解决方法

《MySQL启动报错:InnoDB表空间丢失问题及解决方法》在启动MySQL时,遇到了InnoDB:Tablespace5975wasnotfound,该错误表明MySQL在启动过程中无法找到指定的s... 目录mysql 启动报错:InnoDB 表空间丢失问题及解决方法错误分析解决方案1. 启用 inno

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环... 目录前言一、相关技术背景介绍1、评价对象AOI2、数据处理流程二、对AOI空间范围查询实践1、空间查

利用Python实现时间序列动量策略

《利用Python实现时间序列动量策略》时间序列动量策略作为量化交易领域中最为持久且被深入研究的策略类型之一,其核心理念相对简明:对于显示上升趋势的资产建立多头头寸,对于呈现下降趋势的资产建立空头头寸... 目录引言传统策略面临的风险管理挑战波动率调整机制:实现风险标准化策略实施的技术细节波动率调整的战略价

MySQL表空间结构详解表空间到段页操作

《MySQL表空间结构详解表空间到段页操作》在MySQL架构和存储引擎专题中介绍了使用不同存储引擎创建表时生成的表空间数据文件,在本章节主要介绍使用InnoDB存储引擎创建表时生成的表空间数据文件,对... 目录️‍一、什么是表空间结构1.1 表空间与表空间文件的关系是什么?️‍二、用户数据在表空间中是怎么

CentOS7增加Swap空间的两种方法

《CentOS7增加Swap空间的两种方法》当服务器物理内存不足时,增加Swap空间可以作为虚拟内存使用,帮助系统处理内存压力,本文给大家介绍了CentOS7增加Swap空间的两种方法:创建新的Swa... 目录在Centos 7上增加Swap空间的方法方法一:创建新的Swap文件(推荐)方法二:调整Sww

Python日期和时间完全指南与实战

《Python日期和时间完全指南与实战》在软件开发领域,‌日期时间处理‌是贯穿系统设计全生命周期的重要基础能力,本文将深入解析Python日期时间的‌七大核心模块‌,通过‌企业级代码案例‌揭示最佳实践... 目录一、背景与核心价值二、核心模块详解与实战2.1 datetime模块四剑客2.2 时区处理黄金法

macOS Sequoia 15.5 发布: 改进邮件和屏幕使用时间功能

《macOSSequoia15.5发布:改进邮件和屏幕使用时间功能》经过常规Beta测试后,新的macOSSequoia15.5现已公开发布,但重要的新功能将被保留到WWDC和... MACOS Sequoia 15.5 正式发布!本次更新为 Mac 用户带来了一系列功能强化、错误修复和安全性提升,进一步增