[算法技术]算法的时间复杂度与空间复杂度

1.时间复杂度

算法的时间复杂度是衡量一个算法效率的基本方法。在阅读其他算法教程书的时候,对于算法的时间复杂度的讲解不免有些生涩,难以理解。进而无法在实际应用中很好的对算法进行衡量。

 

 

       《大话数据结构》一书在一开始也针对算法的时间复杂度进行了说明。这里的讲解就非常明确,言简意赅,很容易理解。下面通过《大话数据结构》阅读笔记的方式,通过原因该书的一些简单的例子和说明来解释一下算法的时间复杂度和它的计算方法。

 

       首先从基本定义下手,来了解一下什么是“算法的时间复杂度”,《大话数据结构》一书中对算法的时间复杂度定义如下:

 

               “算法语句总的执行次数 T(n) 是关于问题规模 n 的函数,进而分析 T(n) 随 n 的变化情况并确定 T(n) 的数量级。算法的时间复                杂度,也就是算法的时间度量,记作:T(n) = O(f(n)) 它表示随问题规模 n 的增大,算法执行时间的增长率和f(n) 的增长率                          相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中 f(n) 是问题规模 n 的某个函数。”

 

       光从定义来理解算法的时间复杂度还是比较难的,我们再结合一个简单的例子来说明。计算 1 + 2 + 3 + 4 + ......  + 100 = ? 这样的问题想必大家都遇到过,这里我们通过 C 语言用最简单的方法实现一下这个问题的算法。

 

               int sum = 0, n = 100;        //执行了 1 次

               for (int i = 1; i <= n; i++) {        //执行了 n + 1 次

                       sum += i;        //执行了 n 次

               }

               printf(" sum = %d", sum);        //执行了 1 次

 

       从代码附加的注释可以看到所有代码都执行了多少次。那么这写代码语句执行次数的总和就可以理解为是该算法计算出结果所需要的时间。所以说,上述结算 1 + 2 + 3 + 4 + ......  + 100 = ?的算法所用的时间(算法语句执行的总次数)为 :

 

               1 + ( n + 1 ) + n + 1 = 2n + 3

 

       而当 n 不断增大,比如我们这次所要计算的不是 1 + 2 + 3 + 4 + ......  + 100 = ? 而是 1 + 2 + 3 + 4 + ......  + n = ?其中 n 是一个十分大的数字,那么由此可见,上述算法的执行总次数(所需时间)会随着 n 的增大而增加,但是在 for 循环以外的语句并不受 n  的规模影响(永远都只执行一次)。所以我们可以将上述算法的执行总次数简单的记做:

 

               2n 或者简记  n

 

       这样我们就得到了我们设计的计算 1 + 2 + 3 + 4 + ......  + 100 = ?的算法的时间复杂度,我们把它记作:

 

               O(n)

 

       对于同一个问题,解法通常是不唯一的。比如 1 + 2 + 3 + 4 + ......  + 100 = ?这个问题,还有其他的不少算法。我们再来看一个数学家高斯解决这个问题的算法(想必大家都很熟悉这个故事)。

 

               SUM = 1 + 2 + 3 + 4 + ......  + 100

               SUM = 100 + 99 + 98 + 97 + ...... + 1

               SUM + SUM = 2*SUM = 101 + 101 + 101 + .... + 101          正好 100 个 101

               SUM =  (100*101)/2 = 5050

 

       同样我们将这个解法翻译成 C 语言代码:

 

               int n = 100, sum = 0;        //执行 1 次

               sum = (n*(n + 1))/2;        //执行 1 次

               printf("sum = %d", sum);        //执行 1 次

 

       这样我们针对同一个 1 + 2 + 3 + 4 + ......  + 100 = ?问题,不同的算法又的到了一个算法的时间复杂度:

 

               O(3)    一般记作 O(1) 我们后续给出原因。

 

       从感官上我们就不难看出,从算法的效率上看,O(3) < O(n) 的,所以高斯的算法更快,更优秀(是最优秀的吗?)。

 

       这种用个大写的 O 来代表算法的时间复杂度的记法有个专业的名字叫“大O阶”记法。那么通过对上述的例子进行总结,我们给出算法的时间复杂度(大O阶)的计算方法。

 

               推导“大O阶”的步骤:

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

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

               3、如果最高阶项存在且不是 1 ,则去除与这个项相乘的常数。

 

       下面我们在通过一个有不少 for 循环的例子按照上面给出的推导“大O阶”的方法来计算一下算法的时间复杂度。先看一下下面的这个例子的代码,也是用 C 语言写的,在注释上我们仍然对执行次数进行说明。

 

               int n = 100000;        //执行了 1 次

               for (int i = 0; i < n; i++) {        //执行了 n + 1 次

                       for (int j = 0; j < n; j++) {        //执行了 n*(n+1) 次

                               printf("i = %d, j = %d\n", i, j);        //执行了 n*n 次

                       }

               }

               for (int i = 0; i < n; i++) {        //执行了 n + 1 次

                       printf("i = %d", i);        //执行了 n 次

               }

               printf("Done");        //执行了 1 次

 

       上面的代码严格的说不能称之为一个算法,毕竟它很“无聊而且莫名其妙”(毕竟算法是为了解决问题而设计的嘛),先不论这个“算法”能解决什么问题,我们看一下它的“大O阶”如何推导,还是先计算一下它的执行总次数:

 

               执行总次数 = 1 + (n + 1) + n*(n + 1) + n*n + (n + 1) + 1 = 2n^2 + 3n + 3    这里 n^2 表示 n 的 2次方。

 

       按照上面推导“大O阶”的步骤我们先来第一步:“用常数 1 取代运行时间中的所有加法常数”,则上面的算式变为:

 

               执行总次数 = 2n^2 + 3n + 1    这里 n^2 表示 n 的2次方

 

       第二步:“在修改后的运行次数函数中,只保留最高阶项”。这里的最高阶是 n 的二次方,所以算式变为:

 

               执行总次数 = 2n^2    这里 n^2 表示 n 的2次方

 

       第三步:“如果最高阶项存在且不是 1 ,则去除与这个项相乘的常数”。这里 n 的二次方不是 1 所以要去除这个项的相乘常数,算式变为:

 

               执行总次数 = n^2    这里 n^2 表示 n 的2次方

 

       因此最后我们得到上面那段代码的算法时间复杂度表示为: O( n^2 )        这里 n^2 表示 n 的2次方。

 

       至此,我们对什么是“算法的时间复杂度”和“算法的时间复杂度”表示法“大O阶”的推导方法进行了简单的说明。当然要想在日后的实际工作中快速准确的推导出各种算法的“大O阶”我们还需要进行大量的联系,毕竟熟能生巧嘛。最后我们在把常见的算法时间复杂度以及他们在效率上的高低顺序记录在这里,是大家对算法的效率有个直观的认识。

 

               O(1) 常数阶 < O(logn) 对数阶 < O(n) 线性阶 < O(nlogn) < O(n^2) 平方阶 < O(n^3) < { O(2^n) < O(n!) < O(n^n) }  

 

       最后三项我用大括号把他们括起来是想要告诉大家。如果日后大家设计的算法推导出的“大O阶”是大括号中的这几位,那么趁早放弃这个算法,在去研究新的算法出来吧。因为大括号中的这几位即便是在 n 的规模比较小的情况下仍然要耗费大量的时间,算法的时间复杂度大的离谱,基本上就是“不可用状态”。

 

2.空间复杂度

 

空间复杂度(SpaceComplexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。

                比如直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1)。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息。一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量。

 

                 类似于时间复杂度的讨论,一个算法的空间复杂度(SpaceComplexity)S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。

渐近空间复杂度也常常简称为空间复杂度。

                     空间复杂度(SpaceComplexity)是对一个算法在运行过程中临时占用存储空间大小的量度。一个算法在计算机 存储器上所占用的 存储空间,包括存储算法本身所占用的存储空间,算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。

                    算法的输入输出数据所占用的 存储空间是由要解决的问题决定的,是通过参数表由调用函数传递而来的,它不随本算法的不同而改变。存储算法本身所占用的 存储空间与算法书写的长短成正比,要压缩这方面的存储空间,就必须编写出较短的算法。

                       算法在运行过程中临时占用的 存储空间随算法的不同而异,有的算法只需要占用少量的临时工作单元,而且不随问题规模的大小而改变,我们称这种算法是“就地\"进行的,是节省存储的算法,如这一节介绍过的几个算法都是如此;

                 

                        有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元,例如将在第九章介绍的快速排序和 归并排序算法就属于这种情况。

 

分析一个算法所占用的 存储空间要从各方面综合考虑。如对于 递归算法来说,一般都比较简短,算法本身所占用的 存储空间较少,但运行时需要一个附加 堆栈,从而占用较多的临时工作单元;若写成非递归算法,一般可能比较长,算法本身占用的存储空间较多,但运行时将可能需要较少的存储单元。

 

一个算法的 空间复杂度只考虑在运行过程中为 局部变量分配的 存储空间的大小,它包括为参数表中 形参变量分配的存储空间和为在 函数体中定义的局部变量分配的存储空间两个部分。

若一个算法为 递归算法,其 空间复杂度为递归所使用的 堆栈空间的大小,它等于一次调用所分配的临时 存储空间的大小乘以被调用的次数(即为 递归调用的次数加1,这个1表示开始进行的一次非递归调用)。

算法的 空间复杂度一般也以数量级的形式给出。如当一个算法的 空间复杂度为一个 常量,即不随被处理数据量n的大小而改变时,可表示为O(1);当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为O(log2n);

当一个算法的空间复杂度与n成线性比例关系时,可表示为O(n).若形参为 数组,则只需要为它分配一个存储由 实参传送来的一个地址 指针的空间,即一个 机器字长空间;

若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。

 

3.时间与空间复杂度比较

 

 

对于一个算法,其 时间复杂度和 空间复杂度往往是相互影响的。

当追求一个较好的 时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的 存储空间;反之,当追求一个较好的空间复杂度时,可能会使 时间复杂度的性能变差,即可能导致占用较长的运行时间。

另外,算法的所有性能之间都存在着或多或少的相互影响。因此,当设计一个算法(特别是大型算法)时,要综合考虑算法的各项性能,算法的使用频率,算法处理的数据量的大小,算法描述语言的特性,算法运行的机器系统环境等各方面因素,才能够设计出比较好的算法。算法的时间复杂度和 空间复杂度合称为算法的复杂度。

 

时间: 2024-10-14 18:59:10

[算法技术]算法的时间复杂度与空间复杂度的相关文章

常见排序算法及对应的时间复杂度和空间复杂度

转载请注明出处: http://blog.csdn.net/gane_cheng/article/details/52652705 http://www.ganecheng.tech/blog/52652705.html (浏览效果更好) 排序算法经过了很长时间的演变,产生了很多种不同的方法.对于初学者来说,对它们进行整理便于理解记忆显得很重要.每种算法都有它特定的使用场合,很难通用.因此,我们很有必要对所有常见的排序算法进行归纳. 排序大的分类可以分为两种:内排序和外排序.在排序过程中,全部记

数据结构和算法之时间复杂度和空间复杂度

前言 上一篇<数据结构和算法>中我介绍了数据结构的基本概念,也介绍了数据结构一般可以分为逻辑结构和物理结构.逻辑结构分为集合结构.线性结构.树形结构和图形结构.物理结构分为顺序存储结构和链式存储结构.并且也介绍了这些结构的特点.然后,又介绍了算法的概念和算法的5个基本特性,分别是输入.输出.有穷性.确定性和可行性.最后说阐述了一个好的算法需要遵守正确性.可读性.健壮性.时间效率高和存储量低.其实,实现效率和存储量就是时间复杂度和空间复杂度.本篇我们就围绕这两个"复杂度"展开

php算法基础----时间复杂度和空间复杂度

算法复杂度分为时间复杂度和空间复杂度. 其作用: 时间复杂度是指执行算法所需要的计算工作量: 而空间复杂度是指执行这个算法所需要的内存空间. (算法的复杂性体现在运行该算法时的计算机所需资源的多少上,计算机资源最重要的是时间和空间(即寄存器)资源,因此复杂度分为时间和空间复杂度). 简单来说,时间复杂度指的是语句执行次数,空间复杂度指的是算法所占的存储空间 时间复杂度 计算时间复杂度的方法: 用常数1代替运行时间中的所有加法常数 修改后的运行次数函数中,只保留最高阶项 去除最高阶项的系数 按数量

Python语言算法的时间复杂度和空间复杂度

算法复杂度分为时间复杂度和空间复杂度. 其作用: 时间复杂度是指执行算法所需要的计算工作量: 而空间复杂度是指执行这个算法所需要的内存空间. (算法的复杂性体现在运行该算法时的计算机所需资源的多少上,计算机资源最重要的是时间和空间(即寄存器)资源,因此复杂度分为时间和空间复杂度). 简单来说,时间复杂度指的是语句执行次数,空间复杂度指的是算法所占的存储空间 计算时间复杂度的方法: 用常数1代替运行时间中的所有加法常数 修改后的运行次数函数中,只保留最高阶项 去除最高阶项的系数 按数量级递增排列,

java 时间复杂度和空间复杂度

同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率.算法分析的目的在于选择合适算法和改进算法. 算法复杂度分为时间复杂度和空间复杂度.其作用: 时间复杂度是度量算法执行的时间长短:而空间复杂度是度量算法所需存储空间的大小. 1.时间复杂度 1.1 时间频度 一个算法中的语句执行次数称为语句频度或时间频度.记为T(n) 1.2 时间复杂度 一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)

算法的时间复杂度和空间复杂度-总结

算法的时间复杂度和空间复杂度-总结 通常,对于一个给定的算法,我们要做 两项分析.第一是从数学上证明算法的正确性,这一步主要用到形式化证明的方法及相关推理模式,如循环不变式.数学归纳法等.而在证明算法是正确的基础上,第二部就是分析算法的时间复杂度.算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否.因此,作为程序员,掌握基本的算法时间复杂度分析方法是很有必要的.       算法执行时间需通过依据该算法编制的程序在计算机上运行时所消耗的时间来度量

算法的时间复杂度和空间复杂度详解

通常,对于一个给定的算法,我们要做 两项分析.第一是从数学上证明算法的正确性,这一步主要用到形式化证明的方法及相关推理模式,如循环不变式.数学归纳法等.而在证明算法是正确的基础上,第二部就是分析算法的时间复杂度.算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否.因此,作为程序员,掌握基本的算法时间复杂度分析方法是很有必要的.       算法执行时间需通过依据该算法编制的程序在计算机上运行时所消耗的时间来度量.而度量一个程序的执行时间通常有两种

转 算法的时间复杂度和空间复杂度-总结

http://blog.csdn.net/zolalad/article/details/11848739 通常,对于一个给定的算法,我们要做 两项分析.第一是从数学上证明算法的正确性,这一步主要用到形式化证明的方法及相关推理模式,如循环不变式.数学归纳法等.而在证明算法是正确的基础上,第二部就是分析算法的时间复杂度.算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否.因此,作为程序员,掌握基本的算法时间复杂度分析方法是很有必要的.      

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

<算法的时间复杂度和空间复杂度合称为算法的复杂度> --->算法的时间复杂度 (1)时间频度 一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道.但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了.并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多.一个算法中的语句执行次数称为语句频度或时间频度.记为T(n). (2)时间复杂度 在刚才提到的时间频度中,n称为问题的规模