算法的时间复杂度与空间复杂度

    🌴在我们数据结构系列的第一篇博客当中我们认识到了到底什么是数据结构和算法,那么在本次博客当中我们就来学习一下算法的时间复杂度和空间复杂度应该怎么求解。

    🌳什么是算法的时间复杂度?

    🌵所谓的算法的时间复杂度实际上就是评判算法的运行速度的一个衡量标准。但是呢,一个程序的运行时间也不完全由我们编写的程序的好坏所决定,也和很多因素有关。

    🍀与程序运行的时间开销有关的因素

    🌵就像假如我们使用我们的超级计算机和我们古老的单片机(把一集合了个计算机系统的芯片)同时运行我们的同一个程序,所花费的时间也肯定不相同,很明显我们的超级计算机运行程序所花费的时间会更短一些。

    🌵同时也会和我们的编程语言有关,不同的语言所编写的程序运行时所实际花费的时间也各不相同。但是我们经常说编程语言不分好坏,那着又该怎么解释呢?

    🌵并且我们要想直到一个算法所运行的具体花费的时间往往都会采用事后统计的方式进行试验,但是真的是所有的算法都能试运行测量时间呢?比如测量导弹的轨道算法,我们是不是还应该尝试发射一颗导弹试一下呢?这完全不符合我们的生活需求,况且这样的话花销成本也太大了吧!

    🌵所以我们需要想到的是我们不能单纯的根据运行所花费的时间来评判一个算法的好坏,否则我们的程序较量就会转化为金钱与科技的较量(比谁的装备更加精良)。

    🌾算法的时间复杂度的概念

    🌵为了避免我们这样的事情发生,所以我们的算法的时间复杂度的定义就是是先预估算法的时间开销 T(N) 与我们的问题规模 N 之间的关系(T表示的是时间)。我们这句话该怎么理解呢?通俗一点来说在我们编写程序的时候往往会出现需要我们先输入一连串字符的个数之后在进行输入字符的情况(常见于刷题或者函数传参)。就像下图中的例子中所展示的一样:

    🌵从上面的例题当中我们就可以发现我们先输入一个变量 N 的值,之后才能够根据 N 的值进一步输入我们想要输入的数据的具体内容。而我们之后的输入的数据的规模(数据的数量)又会和我们前面输入的 N 的值息息相关。(N的值不同,后面想要处理的数据个数也各不相同)所以我们的时间复杂度检测一个算法的好坏的方式就是:预估算法的时间开销 T(N) 与我们的问题规模 N 之间的关系(T表示的是时间)。让我们试想一下就算是使用不同的机器,不同的语言,我们比较的只是自身处理数据的变化,那这样对于算法的好坏就会公平许多。再举一个简单的打印数组的函数的例子帮助我们进行理解:

     🌵由于我们的程序是一条一条执行的,执行每一条程序都要花费一定的时间,所以我们所要执行的程序越多我们所要花费的时间就越多,算法的时间开销也就越大。

    🌵在介绍完我们的时间复杂度的初步概念之后,我们再来认识一下怎么判断一个算法的时间按复杂度。

    🌿怎样判断一个算法的时间复杂度?

    🌵我们的时间复杂度分为O(1)的类型和非O(1)的类型,(其实有很多很多,我们在这里先进行一个简单的分类。)我们可以通过对于代码的分析来进行学习算法的时间复杂度:

    🌵就像我们上面的代码一样。我们的时间复杂度就为O(1),因为我们在函数当中执行的时间消耗为常数次。(只要执行的次数为常数次的程序时间复杂度都是O(1))

    🌵而我们非O(1)类型就比较多了,我们还可以分为O(N),O(logN),O(2^N),O(N^2)等等类型。我们同样通过一些例子进行加以理解:

  

    🌵上面是我们递归求阶乘的代码的内容,由于我们每一次递归都会执行时间复杂度为O(1)的代码,并且向下递归N次,所以我们该代码的时间复杂度就为O(N)。

    🌵我们上面的代码的时间复杂度也是O(N),因为我们在for循环当中一共执行了2*N次的程序,我们的时间复杂度的计算和我们程序执行的次数无关,所以属于O(N)量级。可能还有人会问下面不是还有一个m的循环吗?但是我们这个循环的输入和我们的变量无关,属于时间复杂度属于O(1)两级,对于O(N)而言可以省略不计。(当我们循环当中执行的循环次数和我们的形参有关的时候就可以认为我们的时间复杂度至少为O(N)量级)

    

     🌵我们上面的代码的时间复杂度为O(N^2),因为我们第一个for循环的结束条件都是从0到N,两重循环嵌套起来就得到了我们的时间复杂度O(N^2)。(其他部分比如第二个for循环经过上卖弄分析我们的时间复杂度为O(N),下面的为O(1),均远小于我们的O(N^2)所以可以忽略不计。)

   

     🌵我们上面的代码和我们之前的分析相似,N和M作为我们函数的未知数均需要我们传参,所以我们只要和我们的未知数有关的循环那么就需要表示出来。我们上面的代码时间复杂度为:O(N+M)。(如果题目中说明N远大于M或者M远大于N,那么我们就可以将我们较小的未知数省略。)

    🌵举一个比较难的例子:我们上面的代码和我们之前的单独的递归函数很相似,但是我们的分析过程却要复杂很多。我们可以通过下面的一张图进行分析: 

     🌵我们知道的是我们的递归函数进行深层调用都是想上图所示,所以我们就可以做出如上的树状图,那么每当我们生成一个新的节点的时候就会产生两个叶子节点(如果程序没有返回的话)所以我们就可以把上面的函数调用过程看成N的二次方的形式。(我们已经返回的节点对于我们整体的树状结构来看可以忽略不计)所以我们上述代码的时间复杂度就为O(N^2)

  

    🌵上面是我们二分查找的函数实现代码,对于我们二分查找方法熟悉的同伴们肯定知道,所谓的二分查找就是每次砍一半的数据进行一个具体数据的查找。从原理上进行分析我们的时间复杂度也就是我们的程序总运行的次数2^N=M(总次数)。那么我们将程序进行逆序分析可以知道:N=logN(底数2省略,底数不为2就不能省略。),所以我们本代码的时间复杂度就是O(logN)。

    🌵经过上面的代码分析之后相信大家对于时间复杂度的分析已经有了初步的认识了,之后需要的就是多多练习。接下来我们将目标转为空间复杂度。

    🍁空间复杂度

     🌵与时间复杂度相似,我们的空间复杂度需要寻找的是我们在程序当中开辟了多少个新的空间,如果为常数个,那么我们的空间复杂度就为O(1),如果我们开辟的空间和我们传入的变量有关,那么我们的空间复杂度就为O(N)。空间复杂度的一般只有O(1)和O(N)两种,但是不绝对,如果遇到了我们在具体进行分析。再者由于我们的生产条件日渐完善,我们计算机当中的数据存储空间也逐渐增加,所以我们有时候为了提高程序的运行效率也会做出用空间换时间的行为。同样的我们通过具体的代码进行分析:

    🌵我们在上述代码当中开辟了长度为N+1个long long类型的数据的大小。开辟的空间主要和我们的N有关所以我们这段代码的时间复杂度就为O(N)。

    🌵就像我们上述的代码,会在函数当中开辟常数个大小的空间,所以我们上述代码的空间复杂度就为O(1)。

    🌵由于我们的空间复杂度的使用范围远小于我们的时间复杂度,所以我们就不必进行过多的代码展示了,一般对于空间复杂度我们都会作为题目的限制条件进行使用。

    🌵那么本次博客的全部内容如上,感谢您的观看,再见。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿白逆袭记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值