0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一文彻底了解时间复杂度

C语言编程学习基地 来源:51CTO 作者:慕雪年华 2022-08-27 17:51 次阅读

算法在编写成可执行程序的时候,main函数使用这个算法,需要调用一定的空间,消耗一定的时间。算法的效率就是通过空间和时间这两个维度来评判的。

时间复杂度:衡量一个算法的运行速度

空间复杂度:衡量一个算法运行需要开辟的额外空间

那么我们今天先来看看时间复杂度!

79c67cb2-25db-11ed-ba43-dac502259ad0.png

时间复杂度

算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。算法中基本操作的执行次数,为算法的时间复杂度

时间复杂度是一个近似值,并不是实际运行的时间

实际上代码的运行时间,和机器的内存、cpu性能和平台都有关系,同一个代码在不同的机器上运行时间都不一样,如果只以纯粹的时间来考核,很难分析

找到某条基本语句与问题规模N之间的数学表达式,就算出了该算法的时间复杂度

void Test(int N){    int count =0;    for(int i=0;i    {        for(int j=0;j        {            count++;        }    }        for (int k = 0; k < 2 * N ; ++ k)    {        count++;    }        int M = 10;    while (M--)    {        count++;    }        return;}

请问这个代码中,count语句执行了几次?

F ( N ) = N 2 + 2 ∗ N + 10 F(N)=N^2+2*N+10 F(N)=N2+2∗N+10

N = 10 F(N) = 130

N = 100 F(N) = 10210

N = 1000 F(N) = 1002010

你可能会简单地认为,F(N)的结果就是我们的时间复杂度。其实并不然,我们并不需要一个精确的运行次数,只需要知道程序运行次数的量级就行了

这里我们使用大O渐进表示法来表示时间复杂度(空间复杂度同理)

2.1大O的渐进表示法

大O符号(Big O notation):是用于描述函数渐进行为的数学符号

推导大O阶方法:

(1)用常数1取代运行时间中的所有加法常数。

(2)在修改后的运行次数函数中,只保留最高阶项。

(3)如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶

使用这种方法后,test1函数的时间复杂度为

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

对于test1函数,在计算的时候,我们省略了最后的+10,保留了最高阶数N2,即得出了它的时间复杂度

如果最高阶数前面有系数,如2N,系数也将被省略

因为当N的数值很大的时候,后面的那些值对我们总运行次数的影响已经非常小了。大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数

2.2多种情况取最坏

一些算法的时间复杂度会有最好、最坏和平均的情况

最好情况:任意输入规模的最小运行次数(下界)

平均情况:期望的运行次数

最坏情况:任意输入规模的最大运行次数(上界)

举个例子,当我们编写一个在数组中查找数值的算法时,它可能会出现这几种情况:

最好情况:1次就找到

平均情况:N/2次

最坏情况:N次

在实际中的一般情况,我们关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)

2.3常见时间复杂度的计算

NO.1

void Func1(int N){     int count = 0;     for (int k = 0; k < 2 * N ; ++ k)     {      ++count;     }     int M = 10;     while (M--)     {      ++count;     }  printf("%d
", count);}

这里出现了两个循环,分别是2N次和10次。前面提到了大O渐进法只保留最高阶数并省略系数,所以它的时间复杂度是O(N)

NO.2

void Func2(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
", count);}

这里出现了次数为N和M的两层循环:

当M和N差不多大的时候,时间复杂度可以理解为O(M)或O(N)

当M远远大于N时,时间复杂度为O(M)

一般情况取O(M+N)

NO.3 常数阶

void Func3(int N){     int count = 0;     for (int k = 0; k < 100; ++ k)     {      ++count;     }     printf("%d
", count);}

这里我们得知了具体的循环次数为100,是一常数,时间复杂度为O(1),代表常数阶

只要循环次数已知,为一常数且和所传参数无关,其时间复杂度即为O(1)

NO.4 strchr

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

看到这道题的时候,你可能会一愣,strchr是什么?

可这里面没有strchr,但有strstr

strstr函数的作用:在字符串1中寻找是否有字符串2

其中第二个str代表的是string字符串,那我们是不是可以猜想,chr代表的是char字符,其作用是在一个字符串中查找是否有一个字符呢?

当然,光是猜想肯定是不够的,我们还需要求证一下

打开cplusplus网站,搜索strchr,即可转到函数定义

79d4df00-25db-11ed-ba43-dac502259ad0.png

可以看到,该函数的作用是定位字符串中第一次出现该字符的位置,返回值是一个pointer指针

和我们猜想的一样,它的作用就是在字符串中查找一个字符,并返回它第一次出现的位置的地址。

这样一来,strchr函数的时间复杂度就很清楚了,就是遍历字符串所需要的次数,O(N)

NO.5 冒泡排序

voidBubbleSort(int*a,intn){     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;     }}

冒泡排序是一个非常经典的代码,其思路就是遍历整个数组,如果待排序数字大于它的下一位,则交换这两个数字

N个数字的数组需要N-1次排序才能完成

每一次排序都需要遍历一次数组

这样算来,冒泡排序的循环次数就是两个N,即为O(N2)

能否通过循环层级判断?

细心的你可能会发现,上述代码中出现了两层循环,那是不是可以通过循环层级来判断时间复杂度呢?

并不能!

for(int i=0;i{  for(int j=0;j<3;j++)    printf("hehe
");}

如果是上述这种两次循环的情况,会打印3n次呵呵,其时间复杂度是O(N)而不是N2

我们要准确分析算法的思路,并不能简单地通过循环层级来判断时间复杂度

NO.6 二分查找

intBinarySearch(int*a,intn,intx){assert(a);intbegin=0;intend=n-1;while(beginend){intmid=begin+((end-begin)>>1);//使用位移操作符来模拟/2,防止beginend相加后超出int范围if(a[mid]< x)          begin=mid+1;elseif(a[mid]>x)end=mid;elsereturnmid;}return-1;}

二分查找的思路这里不再赘述

假设我们找了x次,每一次查找都会使查找范围减半

N/2/2/2/2 ……

最后我们可以得出2条公式

2 x = N 2^x=N 2x=N

x = l o g 2 N x=log_2N x=log2N

最好情况:O(1)

最坏情况:O(logN)

79e08bac-25db-11ed-ba43-dac502259ad0.png

通过时间复杂度的对比,我们就能看出二分查找的优势在那里了

7a15a288-25db-11ed-ba43-dac502259ad0.png

可以看到,当数据很大的时候,O(logN)的算法执行次数比O(N)少了特别多!!(来自BT-7274的肯定)

NO.7 计算N!

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

对于这个阶乘的递归函数而言,每次函数调用是O(1),时间复杂度主要看递归次数

对于数字N而言,递归需要N次,时间复杂度是O(N)

递归算法时间复杂度计算

如果每次函数调用是O(1),看递归次数

每次函数调用不是O(1),那么就看他递归调用中次数的累加

NO.8 斐波那契数列

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

7a6d0c94-25db-11ed-ba43-dac502259ad0.png

每次递归,次数都会增加,总的来说是以2^x的量级增加的(x代表行数)

1 + 2 + 4 + 8 + … … + 2 X − 2 1+2+4+8+……+2^{X-2} 1+2+4+8+……+2X−2

这里一共有x-1项,用等比数列的求和公式得出,结果为2x-1

所以最后得出的时间复杂度为O(2N)

需要注意的是,当递归调用到底部时,有一些调用会较早退出,这部分位于金字塔的右下角

7a94b848-25db-11ed-ba43-dac502259ad0.png

由于时间复杂度只是一个估算值,这一部分缺失的调用次数对总运行次数的影响不大,故忽略掉

NO.9

void fun(int n) {   int i=l;   while(i<=n)      i=i*2;}

此函数有一个循环,但是循环没有被执行n次,i每次都是2倍进行递增,所以循环只会被执行log2n次

NO.10

给定一个整数sum,从有N个有序元素的数组中寻找元素a,b,使得a+b的结果最接近sum,最快的平均时间复杂度是?

A. O(n)//√B. O(n^2)C. O(nlogn)D. O(logn)

数组元素有序,所以a,b两个数可以分别从开始和结尾处开始搜,根据首尾元素的和是否大于sum,决定搜索的移动,整个数组被搜索一遍,就可以得到结果,所以最好时间复杂度为n

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4562

    浏览量

    92123
  • 函数
    +关注

    关注

    3

    文章

    4245

    浏览量

    62050
  • 复杂度
    +关注

    关注

    0

    文章

    8

    浏览量

    7897

原文标题:【算法】几分钟时间让你彻底学会—时间复杂度!

文章出处:【微信号:cyuyanxuexi,微信公众号:C语言编程学习基地】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    PCB与PCBA工艺复杂度的量化评估与应用初探!

    , 不知道如何区分普通和复杂的PCB和 PCBA的设计,并采用什么样的方式来处理。 基于上述考虑, 我们参考了业 界已有的作法, 设计了个PCB 和 PCBA的工艺复杂度计算公式以解决这 方面
    发表于 06-14 11:15

    JEM软件复杂度的增加情况

    这篇文档展示了几个机构关于JEM软件复杂度的增加情况的看法,特别提出来创立个新的Ad-hoc组,研究降低软件般性复杂度的可能方法。
    发表于 07-19 08:25

    时间复杂度是指什么

    原理->微机原理->软件工程,编译原理,数据库数据结构1.时间复杂度时间复杂度是指执行算法所需要的计算工作量,因为整个算法的执行时间与基本操
    发表于 07-22 10:01

    各种排序算法的时间空间复杂度、稳定性

    各种排序算法的时间空间复杂度、稳定性、排序算法分类:二、排序算法比较:注:1、归并排序可以通过手摇算法将空间复杂度降到O(1),但是时间
    发表于 12-21 07:48

    图像复杂度对信息隐藏性能影响分析

    针对信息隐藏中载体图像的差异性,提出种图像复杂度评价方法,综合考虑图像的压缩特性以及图像纹理能量作为图像复杂度指标,并基于阈值划分准则对栽体图像进行复杂度分类,以几种经典的基于直方图
    发表于 11-14 09:57 5次下载

    深度剖析时间复杂度

    相信每位录友都接触过时间复杂度,但又对时间复杂度的认识处于种朦胧的状态,所以是时候对
    的头像 发表于 03-18 10:18 1802次阅读

    如何求递归算法的时间复杂度

    那么我通过道简单的面试题,模拟面试的场景,来带大家逐步分析递归算法的时间复杂度,最后找出最优解,来看看同样是递归,怎么就写成了O(n)的代码。
    的头像 发表于 07-13 11:30 2182次阅读

    如何求递归算法的时间复杂度

    相信很多同学对递归算法的时间复杂度都很模糊,那么这篇Carl来给大家通透的讲讲。
    的头像 发表于 07-13 11:33 1533次阅读

    算法之空间复杂度

    算法之空间复杂度:衡量个算法运行需要开辟的额外空间
    的头像 发表于 08-31 10:29 1518次阅读

    常见机器学习算法的计算复杂度

    时间复杂度不是测量个算法或段代码在某个机器或者条件下运行所花费的时间时间
    发表于 10-02 12:45 768次阅读

    算法时空复杂度分析实用指南1

    我以前的文章主要都是讲解算法的原理和解题的思维,对时间复杂度和空间复杂度的分析经常笔带过,主要是基于以下两个原因:
    的头像 发表于 04-12 14:37 456次阅读
    算法时空<b class='flag-5'>复杂度</b>分析实用指南1

    算法时空复杂度分析实用指南2

    类似的,想想之前说的数据结构扩容的场景,也许`N`次操作中的某次操作恰好触发了扩容,导致时间复杂度提高,但总的时间复杂度依然保持在`O(N
    的头像 发表于 04-12 14:38 461次阅读
    算法时空<b class='flag-5'>复杂度</b>分析实用指南2

    算法时空复杂度分析实用指南(上)

    本文会篇幅较长,会涵盖如下几点: 1、Big O 表示法的几个基本特点。 2、非递归算法中的时间复杂度分析。 3、数据结构 API 的效率衡量方法(摊还分析)。 4、递归算法的时间/空间
    的头像 发表于 04-19 10:34 682次阅读
    算法时空<b class='flag-5'>复杂度</b>分析实用指南(上)

    算法时空复杂度分析实用指南(下)

    Big O 表示法的几个基本特点。 2、非递归算法中的时间复杂度分析。 3、数据结构 API 的效率衡量方法(摊还分析)。 4、递归算法的时间/空间复杂度的分析方法,
    的头像 发表于 04-19 10:35 576次阅读
    算法时空<b class='flag-5'>复杂度</b>分析实用指南(下)

    如何计算时间复杂度

    1 算法与时间复杂度 算法(Algorithm)是求解个问题需要遵循的,被清楚指定的简单指令的集合。 算法旦确定,那么下步就要确定该算
    的头像 发表于 10-13 11:19 2375次阅读
    如何计算<b class='flag-5'>时间</b><b class='flag-5'>复杂度</b>