【C语言 数据机构】时间复杂度与空间复杂度

news2024/11/18 16:39:15

文章目录

  • 时间复杂度
  • 空间复杂度

时间复杂度

  • 判断一个算法所编程序运行时间的多少,并不是将程序编写出来,通过在计算机上运行所消耗的时间来度量。原因很简单,一方面,解决一个问题的算法可能有很多种,一一实现的工作量无疑是巨大的,得不偿失;另一方面,不同计算机的软、硬件环境不同,即便使用同一台计算机,不同时间段其系统环境也不相同,程序的运行时间很可能会受影响,严重时甚至会导致误判。

  • 实际场景中,我们更喜欢用一个估值来表示算法所编程序的运行时间。所谓估值,即估计的、并不准确的值。注意,虽然估值无法准确的表示算法所编程序的运行时间,但它的得来并非凭空揣测,需要经过缜密的计算后才能得出。

  • 也就是说,表示一个算法所编程序运行时间的多少,用的并不是准确值(事实上也无法得出),而是根据合理方法得到的预估值。


预估一个算法所编程序的运行时间

  • 先分别计算程序中每条语句的执行次数,然后用总的执行次数间接表示程序的运行时间。
for(int i = 0 ; i < n ; i++)     //<- 从 0 到 n,执行 n+1 次
{
    a++;                         //<- 从 0 到 n-1,执行 n 次
}

可以看到,这段程序中仅有 2 行代码,其中:

  • for 循环从 i 的值为 0 一直逐增至 n(注意,循环退出的时候 i 值为 n),因此 for 循环语句执行了 n+1 次;
  • 而循环内部仅有一条语句,a++ 从 i 的值为 0 就开始执行,i 的值每增 1 该语句就执行一次,一直到 i 的值为 n-1,因此,a++ 语句一共执行了 n 次。

因此,整段代码中所有语句共执行了 (n+1)+n 次,即 2n+1 次。数据结构中,每条语句的执行次数,又被称为该语句的频度。整段代码的总执行次数,即整段代码的频度。


再举一个例子:

for(int i = 0 ; i < n ; i++)           // n+1
{ 
    for(int j = 0 ; j < m ; j++)       // n*(m+1)
    {
        num++;                         // n*m
    }
}
  • 计算此段程序的频度为:(n+1)+n*(m+1)+n*m,简化后得 2*n*m+2*n+1。值得一提的是,不同程序的运行时间,更多场景中比较的是在最坏条件下程序的运行时间。以上面这段程序为例,最坏条件即指的是当 n、m 都为无限大时此段程序的运行时间。

  • 要知道,当 n、m 都无限大时,我们完全就可以认为 n==m。在此基础上,2*n*m+2*n+1 又可以简化为 2*n2+2*n+1,这就是此段程序在最坏情况下的运行时间,也就是此段程序的频度。

如果比较以上 2 段程序的运行时间,即比较 2n+1 和 2*n2+2*n+1 的大小,显然当 n 无限大时,前者要远远小于后者

在这里插入图片描述


以 2n+1 为例,当 n 无限大时,是否在 2n 的基础上再做 +1 操作,并无关紧要,因为 2n 和 2n+1 当 n 无限大时,它们的值是无限接近的。甚至于我们还可以认为,当 n 无限大时,是否给 n 乘 2,也是无关紧要的,因为 n 是无限大,2*n 也是无限大。

再以无限大的思想来简化 2*n2+2*n+1。当 n 无限大的:

  • 首先,常数 1 是可以忽略不计的;
  • 其次,对于指数级的 2*n2 来说,是否在其基础上加 2*n,并无关紧要;
  • 甚至于,对于是否给 n2 乘 2,也可以忽略。

因此,最终频度 2*n2+2*n+1 可以简化为 n2 。

在数据结构中,频度表达式可以这样简化:

  • 去掉频度表达式中,所有的加法常数式子。例如 2n2+2n+1 简化为 2n2+2n ;
  • 如果表达式有多项含有无限大变量的式子,只保留一个拥有指数最高的变量的式子。例如 2n2+2n 简化为 2n2;

如果最高项存在系数,且不为 1,直接去掉系数。例如 2n2 系数为 2,直接简化为 n2 ;


常见的时间复杂度量级有:

  • 常数阶O(1)
  • 对数阶O(logN)
  • 线性阶O(n)
  • 线性对数阶O(nlogN)
  • 平方阶O(n²)
  • 立方阶O(n³)
  • K次方阶O(n^k)
  • 指数阶(2^n)

常数阶O(1):无论代码执行了多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就都是O(1),如:

int i = 1;
int j = 2;
++i;
j++;
int m = i + j;

上述代码在执行的时候,它消耗的时候并不随着某个变量的增长而增长,那么无论这类代码有多长,即使有几万几十万行,都可以用O(1)来表示它的时间复杂度。


线性阶O(n):这个在最开始的代码示例中就讲解过了,如:

for(i=1; i<=n; ++i)
{
   j = i;
   j++;
}

这段代码,for循环里面的代码会执行n遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度。


对数阶O(logN):还是先来看代码:

int i = 1;
while(i<n)
{
    i = i * 2;
}

从上面代码可以看到,在while循环里面,每次都将 i 乘以 2,乘完之后,i 距离 n 就越来越近了。我们试着求解一下,假设循环x次之后,i 就大于 2 了,此时这个循环就退出了,也就是说 2 的 x 次方等于 n,那么 x = log2^n也就是说当循环 log2^n 次以后,这个代码就结束了。因此这个代码的时间复杂度为:O(logn)


线性对数阶O(nlogN):将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 n * O(logN),也就是了O(nlogN)。

for(m=1; m<n; m++)
{
    i = 1;
    while(i<n)
    {
        i = i * 2;
    }
}

平方阶O(n²):就更容易理解了,如果把 O(n) 的代码再嵌套循环一遍,它的时间复杂度就是 O(n²) 了。
举例:

for(x=1; i<=n; x++)
{
   for(i=1; i<=n; i++)
    {
       j = i;
       j++;
    }
}

这段代码其实就是嵌套了2层n循环,它的时间复杂度就是 O(n*n),即 O(n²)如果将其中一层循环的n改成m,即:

for(x=1; i<=m; x++)
{
   for(i=1; i<=n; i++)
    {
       j = i;
       j++;
    }
}

那它的时间复杂度就变成了 O(m*n)


立方阶O(n³)、K次方阶O(n^k)

  • 参考上面的O(n²) 去理解就好了,O(n³)相当于三层n循环,其它的类似。
    除此之外,其实还有 平均时间复杂度、均摊时间复杂度、最坏时间复杂度、最好时间复杂度 的分析方法,有点复杂,这里就不展开了。

空间复杂度

要知道每一个算法所编写的程序,运行过程中都需要占用大小不等的存储空间,例如:

  • 程序代码本身所占用的存储空间;
  • 程序中如果需要输入输出数据,也会占用一定的存储空间;
  • 程序在运行过程中,可能还需要临时申请更多的存储空间。

首先,程序自身所占用的存储空间取决于其包含的代码量,如果要压缩这部分存储空间,就要求我们在实现功能的同时,尽可能编写足够短的代码。

程序运行过程中输入输出的数据,往往由要解决的问题而定,即便所用算法不同,程序输入输出所占用的存储空间也是相近的。

事实上,对算法的空间复杂度影响最大的,往往是程序运行过程中所申请的临时存储空间。不同的算法所编写出的程序,其运行时申请的临时存储空间通常会有较大不同。

int n;
scanf("%d", &n);
int a[10];

通过分析不难看出,这段程序在运行时所申请的临时空间,并不随 n 的值而变化。而如果将第 3 行代码改为:

int a[n];

此时,程序运行所申请的临时空间,和 n 值有直接的关联。

所以,如果程序所占用的存储空间和输入值无关,则该程序的空间复杂度就为 O(1);反之,如果有关则需要进一步判断它们之间的关系:

  • 如果随着输入值 n 的增大,程序申请的临时空间成线性增长,则程序的空间复杂度用 O(n) 表示;
  • 如果随着输入值 n 的增大,程序申请的临时空间成 n2 关系增长,则程序的空间复杂度用 O(n2) 表示;
  • 如果随着输入值 n 的增大,程序申请的临时空间成 n3 关系增长,则程序的空间复杂度用 O(n3) 表示;

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

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

相关文章

cocoapods安装失败到成功的记录贴

mac系统版本&#xff1a;10.15.5 (19F101) 最优解安装顺序&#xff1a;Xcode > HomeBrew > RVM > Ruby > CocoaPods 1. 安装方案1(百度常用法&#xff09; 1.1 更新gems和换国产源&#xff1a; RubyGems 镜像 - Ruby Chinahttps://gems.ruby-china.co…

使用VBA获取电脑MAC地址

实例需求&#xff1a;如何使用VBA读取电脑的MAC地址&#xff0c;包含有线网卡和无线网卡。 这个需求看似有些无厘头&#xff0c;为嘛要用VBA来读取MAC地址&#xff0c;存在的就是合理的。例如使用MAC地址和其他硬件信息可以生成电脑的唯一识别号&#xff0c;用于软件注册和实现…

Vue Node

Vue配置代理服务器 一、运行后台服务 启动后台Node服务器&#xff0c;运行后台程序&#xff0c;学习资料node代码&#xff0c;服务5000开启 FeHelper - Awesome 二、Ajax请求 xhr 【不常用】Windows 内部 new XMLHttpRequest()xhr.open() xhr.send()内部公司封装xhr开源封装…

力扣 2325. 解密消息

题目 给你字符串 key 和 message &#xff0c;分别表示一个加密密钥和一段加密消息。解密 message 的步骤如下&#xff1a; 使用 key 中 26 个英文小写字母第一次出现的顺序作为替换表中的字母 顺序 。 将替换表与普通英文字母表对齐&#xff0c;形成对照表。 按照对照表 替换…

OAuth2 01

目录 1.什么是OAuth 2.OAuth2中的角色 3.认证流程 4.生活中的OAuth2思维 5.令牌的特点 6.OAuth2的授权方式 6.1 OAuth2授权码 6.2 隐藏方式 6.3 密码方式 6.4 凭证方式 1.什么是OAuth2 1.OAuth2.0介绍 OAuth&#xff08;Open Authorization&#xff09;是一个关于授权&…

Android 抓包相关 SSL相关

https无法明文抓包 Android P版本开始强制App使用Https协议&#xff0c;否则访问崩溃如下所示错误&#xff1a; java.lang.ClassCastException: com.android.okhttp.internal.huc.HttpURLConnectionImpl cannot be cast to javax.net.ssl.HttpsURLConnection可参阅&#xff…

C 语言零基础入门教程(二十三)

C 可变参数 有时&#xff0c;您可能会碰到这样的情况&#xff0c;您希望函数带有可变数量的参数&#xff0c;而不是预定义数量的参数。C 语言为这种情况提供了一个解决方案&#xff0c;它允许您定义一个函数&#xff0c;能根据具体的需求接受可变数量的参数。下面的实例演示了…

Centos8中安装配置php

一、问题描述Centos8中我们在使用Apache部署配置网站的时候&#xff0c;发现Apache服务已经正常启动且网站也配置完成到Apache主目录中&#xff0c;但是访问时网站却不能正常运行【即&#xff1a;只能够以列表的方式列出所有网站的资源文件&#xff0c;而不是以网页的形式展现】…

关于荧光素76863-28-0,FITC-5-thiosemicarbazide,荧光素-5-氨基硫脲 相关知识分享

荧光素-5-氨基硫脲&#xff0c;Fluorescein-5-thiosemicarbazide&#xff0c;FITC-5-thiosemicarbazide荧光素-5-氨基硫脲是一种含胺的荧光探针&#xff0c;可用于标记糖和蛋白质羰基衍生物Product specifications&#xff1a;1.CAS No&#xff1a;76863-28-02.Molecular formu…

超越OCR的富文档内容解析神器LayoutParser

论文题目&#xff1a;《A unified toolkit for Deep Learning Based Document Image Analysis》 论文链接&#xff1a;https://arxiv.org/abs/2103.15348 论文官方网站&#xff1a;https://layout-parser.github.io/ 论文开源项目&#xff1a;https://github.com/Layout-Par…

Ubuntu 18.04安装配置MySQL数据库

文章目录1. 安装MySQL数据库2. 配置MySQL数据库3. 远程访问设置4. Navicat连接MySQL数据库1. 安装MySQL数据库 这里可以通过包管理工具apt安装MySQL数据库&#xff0c;在ubuntu18.04下mysql版本默认为5.7。 安装命令如下&#xff1a; sudo apt-get install mysql-server安装…

【Echarts雪花宝典特殊示例100+】 目录

目前已发表2篇示例文章vueecharts系列教程旨在为开发者提供简单快捷的代码示例&#xff0c;复制即可用。在每一个示例中&#xff0c;解释相应的API知识点&#xff0c;做到简易实现&#xff0c;轻松学会。 通常一个Echarts图表通常由title(标题)、legend(图例)、grid&#xff0…

ESP-IDF:插入排序和希尔排序测试

代码&#xff1a; /插入排序和希尔排序测试/ void printArray14(int arr[], int len) { for (int i 0; i < len; i) { cout << arr[i] << " "; } cout << endl; } void insertSort(int arr[], int start, int end) { // 无序插入有序队列&am…

BM19 寻找峰值

目录 描述 示例1 思路&#xff1a; 代码&#xff1a; 描述 给定一个长度为n的数组nums&#xff0c;请你找到峰值并返回其索引。数组可能包含多个峰值&#xff0c;在这种情况下&#xff0c;返回任何一个所在位置即可。 1.峰值元素是指其值严格大于左右相邻值的元素。严格…

专访三维空间雷成老师 | 原来水墨画风格的3D建筑动画可以如此惊艳……

CGarchitect 是业界赫赫有名的国际3D建筑赛事&#xff0c;2005年首次举办至今已成功举办了17届大赛&#xff0c;每年都吸引了全球许多知名的建筑设计工作室、动画工作室、艺术家及学生参赛。2021年的CGarchitect奖项类别包括图像组、影片组和交互组&#xff0c;其中图像组和影片…

【Rust】17. Rust 中的并发

17.1 线程 17.1.1 spawn&#xff1a;创建新线程 thread::spawn&#xff1a;创建一个新线程&#xff0c;需要传递一个闭包&#xff0c;并在其中包含希望在新线程运行的代码thread::sleep&#xff1a;调用强制线程停止执行一小段时间。比如&#xff1a;thread::sleep(Duration::…

深入理解MySql(一)MySql视图、存储过程、预处理语句、触发器、定时器

MySql视图、存储过程、预处理语句、触发器、定时器 1、视图 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 视图只保存了查询的…

iOS面试- 0x02 WebView

有了UIWebView&#xff0c;为什么还需要WKWebView&#xff1f; UIWebVieW的缺点&#xff1a; 笨重难用、内存泄露、内存消耗大&#xff0c;性能差 —— WKWebView提高性能 WKWebView 拥有60fps滚动刷新率和safari相同的js引擎等优势。 1、WKWebView 白屏问题 WKWebView是一个多…

ElasticSearch6.x版本的Scroll滚动查询讲解及Kibana和SpringBoot实操演示

文章目录一、Scroll滚动查询介绍二、Kibana上操作三、SpringBoot中操作四、总结一、Scroll滚动查询介绍 ElasticSearch中在进行普通的查询时&#xff0c;默认只会查询出来10条数据。我们通过设置ElasticSearch中的size可以将最终的查询结果从10增加到10000。但这时候如果我们需…

Sensor曝光和帧率基础知识

Sensor曝光和帧率基础知识1. 简介2. H_BLANK和V_BLANK3. 曝光原理3.1 Sensor逐行曝光基本原理3.2 Sensor全局曝光基本原理4. 曝光时间计算公式4.1 曝光一行的时间line_timeline\_timeline_time4.2 曝光一帧的时间exposure_timeexposure\_timeexposure_time4.3 帧率(fps)的计算1…