01数据结构-时间复杂度和空间复杂度
01数据结构-时间复杂度和空间复杂度
- 前言
- 1.“好”算法的标准
- 2.时间频度
- 3.时间复杂度
- 3.1时间复杂度解决具体步骤
- 3.2例子分析
- 3.3最好时间复杂度
- 3.4最坏时间复杂度
- 3.5平均情况时间复杂度
- 3.6均摊复杂度
- 4.空间复杂度
前言
⼀般来说,解决问题的⽅法不⽌⼀种。我们需要学习如何⽐较不同算法的性能,并选择最佳算法来解决特定的问题。⼀个算法的好坏,我们可以从时间和空间两个维度去衡量。执⾏时间越短,占⽤内存空间越⼩的算法,我们认为是更优的算法。并且,⼀般分为两个阶段,⼀是算法完成前的理论分析,⼆是算法完成后实际分析。
「理论分析」:这种算法的效率分析是通过假设所有其他因素,如处理器的速度等是恒定的,对算法的实现没有影响。
「实际分析」:当算法实现后,我们需要考虑算法采⽤编程语⾔,然后在特定计算机上执⾏该算法,其消耗的时间与计算机的硬件⽔平相关。在此分析中,我们要收集实际的统计数据,如运⾏时间和所需空间
1.“好”算法的标准
解决⼀个问题的⽅法可能有很多,但能称得上算法的,⾸先它必须能彻底解决这个问题(称为准确性),且根据其编写出的程序在任何情况下都不能崩溃(称为健壮性)。
注意,程序和算法是完全不同的概念。算法是解决某个问题的想法、思路;⽽程序是在根据算法编写出来的真正可以运⾏的代码。例如,要依次输出⼀维数组中的数据元素的值,⾸先想到的是使⽤循环结构,在这个算法的基础上,我们才开始编写程序。
在满⾜准确性和健壮性的基础上,还有⼀个重要的筛选条件,即通过算法所编写出的程序的运⾏效率。程序的运⾏效率具体可以从 2 个⽅⾯衡量,分别为:
- 程序的运⾏时间。
- 程序运⾏所需内存空间的⼤⼩。
根据算法编写出的程序,运⾏时间更短,运⾏期间占⽤的内存更少,该算法的运⾏效率就更⾼,算法也就更好。
那么,如何衡量⼀个算法所编写出程序的运⾏效率呢?数据结构中,⽤时间复杂度来衡量程序运⾏时间的多少;⽤空间复杂度来衡量程序运⾏所需内存空间的⼤⼩。
2.时间频度
⼀个算法执⾏所耗费的时间,从理论上是不能算出来的,必须上机运⾏测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且⼀个算法花费的时间与算法中语句的执⾏次数成正⽐例,哪个算法中语句执⾏次数多,它花费时间就多。⼀个算法中的语句执⾏次数称为语句频度或时间频度。记为T(n)
3.时间复杂度
在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引⼊时间复杂度概念。 ⼀般情况下,算法中基本操作重复执⾏的次数是问题规模n的某个函数,⽤T(n)表示,若有某个辅助函数f(n),使得当n趋近于⽆穷⼤时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n))为算法的渐进时间复杂度,简称时间复杂度。常⻅的算法时间复杂度由⼩到⼤依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)
3.1时间复杂度解决具体步骤
1、找出算法中的基本语句;算法中执⾏次数最多的那条语句就是基本语句,通常是最内层循环的循环体。
2、计算基本语句的执⾏次数的数量级;只需计算基本语句执⾏次数的数量级,这就意味着只要保证基本语句执⾏次数的函数中的最⾼次幂正确即可,可以忽略所有低次幂和最⾼次幂的系数。这样能够简化算法分析,并且使注意⼒集中在最重要的⼀点上:增⻓率。
3、⽤⼤Ο记号表示算法的时间性能。将基本语句执⾏次数的数量级放⼊⼤Ο记号中。
3.2例子分析
-
O(1)
- Temp=i; i=j; j=temp;
- 以上三条单个语句的频度均为1,该程序段的执⾏时间是⼀个与问题规模n⽆关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。注意:如果算法的执⾏时间不随着问题规模n的增加⽽增⻓,即使算法中有上千条语句,其执⾏时间也不过是⼀个较⼤的常数。此类算法的时间复杂度是O(1)。
-
一次循环
最开始我们写慢一点,要解决一重循环的问题,我们有三步: -
1.写出循环终止条件
- i(终止变量)>n(数据规模)
-
2.找到循环次数和循环终止变量的关系
- 发现循环次数和终止变量之间存在一个2t的关系
-
3.联立等式,推到关系
- i=2t(由循环次数和终止变量关系得)
- i>n(由循环终止条件得)
-
最终得出时间复杂度
后面一次循环的算时间复杂度的问题都可以这么做。
接下来看几道例题
这个减一可以忽视,当n趋于无限大的时候,n2+n+1…我们只关心最高次幂,后面对其的影响很小。所以这题选A
这里同理当n趋于无穷大的时候我们只关心高次幂,即t2。
- 双重循环
要解决双重循环的问题,我们有三步:
- 1.构建一个矩阵,把外层循环的执行次数当成第一行
- 外层循环次数为n
- 2.把内层循环的执行次数当成每一下行的列数
- 内层循环的次数随着外层循环的次数变化而变化
- 3.这个二维矩阵的节点(格子)数量就是循环次数
- 最终所有节点加起来就是循环次数。
- 最终得出时间复杂度
接下来看几道例题:
- 先写出外层循环数据k与次数t的关系,发现每多循环一次,k就乘以2,则说明k=2t,而k要小于等于n(数据规模),建立两者之间的关系:2t<=n,说明外层应该循环了log2n。
- 内层循环好算,直接是n次
- 最终执行次数n*log2n 选C。
- 由上面那道题的基础我们很容易算出外层循环了log2n次
- 而内层循环,j随着i的变化而变化,每一列的次数很明显是一个以1为首项,2为公比的等比数列由等比数列求和公式:Sn=a1(1-qn)/1-q带入a1=1,q=2,化简得2n-1数据趋于无穷大的时候可以省略掉-1,注意这里的n是项数,而项数就是我们行数算出来的log2n,带入得Sn为n。
- Sn为n代表这个矩阵的所有节点数是n,故选B。
3.3最好时间复杂度
- 最好情况时间复杂度就是,在最理想的情况下,执⾏这段代码的时间复杂度。在最理想的情况下,要查找的变量 x 正好是数组的第⼀个元素,这个时候对应的时间复杂度就是最好情况时间复杂度。
3.4最坏时间复杂度
- 最坏情况时间复杂度就是,在最糟糕的情况下,执⾏这段代码的时间复杂度。如果数组中没有要查找的变量 x,我们需要把整个数组都遍历⼀遍才⾏,所以这种最糟糕情况下对应的时间复杂度就是最坏情况时间复杂度。我们平常算的大O表示法算的就是最坏时间复杂度。
3.5平均情况时间复杂度
- 我们都知道,最好情况时间复杂度和最坏情况时间复杂度对应的都是极端情况下的代码复杂度,发⽣的概率其实并不⼤。为了更好地表示平均情况下的复杂度,我们引⼊另⼀个概念:平均情况时间复杂度。
3.6均摊复杂度
- 均摊复杂度(Amortized Complexity)是一种用于分析算法或数据结构性能的技术,核心是通过将一系列操作的总成本分摊到每个操作上,评估其平均性能,尤其适用于少数高成本操作与多数低成本操作交替出现的场景,且不涉及概率,可保证最坏情况下每个操作的平均性能
以上从3.3到3.6我只介绍个概念即可,感兴趣的可以自行查阅。
4.空间复杂度
类似于时间复杂度的讨论,⼀个算法的空间复杂度(Space Complexity)S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。渐近空间复杂度也常常简称为空间复杂度。
空间复杂度(Space Complexity)是对⼀个算法在运⾏过程中临时占⽤存储空间⼤⼩的量度。⼀个算法在计算机存储器上所占⽤的存储空间,包括存储算法本身所占⽤的存储空间,算法的输⼊输出数据所占⽤的存储空间和算法在运⾏过程中临时占⽤的存储空间这三个⽅⾯。算法的输⼊输出数据所占⽤的存储空间是由要解决的问题决定的,是通过参数表由调⽤函数传递⽽来的,它不随本算法的不同⽽改变。存储算法本身所占⽤的存储空间与算法书写的⻓短成正⽐,要压缩这⽅⾯的存储空间,就必须编写出较短的算法。算法在运⾏过程中临时占⽤的存储空间随算法的不同⽽异,有的算法只需要占⽤少量的临时⼯作单元,⽽且不随问题规模的⼤⼩⽽改变,我们称这种算法是“就地"进⾏的,是节省存储的算法;有的算法需要占⽤的临时⼯作单元数与解决问题的规模n有关,它随着n的增⼤⽽增⼤,当n较⼤时,将占⽤较多的存储单元,例如快速排序和归并排序算法就属于这种情况。
如当⼀个算法的空间复杂度为⼀个常量,即不随被处理数据量n的⼤⼩⽽改变时,可表示为O(1);当⼀个算法的空间复杂度与以2为底的n的对数成正⽐时,可表示为0(10g2n);当⼀个算法的空I司复杂度与n成线性⽐例关系时,可表示为0(n).若形参为数组,则只需要为它分配⼀个存储由实参传送来的⼀个地址指针的空间,即⼀个机器字⻓空间;若形参为引⽤⽅式,则也只需要为其分配存储⼀个地址的空间,⽤它来存储对应实参变量的地址,以便由系统⾃动引⽤实参变量。
因为空间复杂度我们不咋用到,所以我这里只是复制粘贴了一下概念,大家想了解更深的话可以去看其他的博客或课程。
本次博客重点还是3.1和3.2怎么算时间复杂度这里,大概先写这些吧,今天的博客就先写到这,谢谢您的观看。