算法学习笔记一 : 算法初体验——时间复杂度和空间复杂度
一、什么是算法?
瑞士著名的科学家N.Wirth曾经提出:
程序 = 数据结构 + 算法
数据结构是程序的骨架,算法是程序的灵魂。
在我们日常生活中,算法无处不在。每天的时间如何规划,我们手中所拥有的资源如何利用,等等,其实都是一种算法。也就是说,哪怕不是计算机领域的专业人员,也在每天与算法打交道!
那么,究竟什么是算法呢?
算法,是指对特定问题的求解方法
它可以用自然语言,程序设计语言(Java,c++,python等等)来实现,也可以用流程图,框架图等等去表示。
算法应该具有如下的特性:
-
有穷性(Finiteness) 算法的有穷性是指算法必须能在执行有限个步骤之后终止;
-
确切性(Definiteness) 算法的每一步骤必须有确切的定义;
-
输入项(Input) 一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;
-
输出项(Output) 一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
-
可行性(Effectiveness) 算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)。
二、“好算法”的标准
对于一个好的算法,应该有下图的特点:
从名字我们不难看出其背后所代表的含义,在这里就不多赘述了。在平时评判算法的时候,一般会将更多的精力放在高效性上,既评判算法的效率。
事后统计:利用计算机的计时功能,不同算法的程序可以用一组或多组相同的统计数据区分,但是它需要先运行一句算法编制的程序,并且所得时间依赖于硬件、软件等环境因素,可能会掩盖了算法本身的优劣,因此,在大多数时候,我们通常不会使用这种方法去评估算法的效率。
事前分析估计:一个高级语言程序在计算机上所运行消耗的时间取决于:
- 算法所使用的策略
- 问题的规模
- 程序语言
- 编译程序产生机器代码质量
- 机器执行指令速度
由此我们不难看出,同一种算法用不同的语言,不同的编译程序、在不同的机器上运行,效率均不同,因此使用绝对时间单位衡量是不合适的。
因此人们引出了时间复杂度和空间复杂度两个度量标准去衡量一个程序的效率。
1.时间复杂度
时间复杂度:算法运行所需要的时间,一般将算法的执行次数作为评判标准,举个简单的例子:
sum = 0 //运行1次
total = 0 //运行1次
for (int i = 1 ;i <= n;i++){ //运行n次
sum = sum + i; //运行n次
for(int j = 1;j < n;j++){ //运行n×n次
total = total + i * j;//运行n×n次
}
}
把算法所有的运行次数加起来:1+1+n+n+n×n+n×n
我们用一个函数表达:T(n)= 2n²+2n+2,n也就是我们所说的问题规模
当n足够大的时候,我们会发现,影响这个函数取值最大的因素应该是多项式中次数最高的那一项,低次项的甚至可以忽略不计。也就是说,我们可以找出问题规模中次数最高的一项用于大致粗略的估计时间复杂度。联想到我们不可能将所有程序的执行次数都一一数出,因此用次数最高的项去估计时间复杂度,是较为可行的。人们用下图的这种渐进复杂度对算法运行次数进行了粗略的估计,大致了反映了问题规模增长的趋势。
有些算法,比如排序、查找、插入等算法,可以分为最好,最坏和平均情况计算渐进复杂度,但我们一般考虑最坏的情况,最坏的情况对于实际衡量算法的好坏具有很大的意义
那么一般我们是如何计算时间复杂度的呢?
根据我们上面的描述,我们不难看出,只需要找到次数最高的项,我们便可以比较直观的看出时间复杂度的大小,在程序中,我们所需要找的,就是那些嵌套最深层的语句频度
同样的,我们也举一个简单的例子
void exam ( float x[ ][ ], int m, int n ) {
float sum [ ];
for ( int i = 0; i < m; i++ ) {
sum[i] = 0.0;
for ( int j = 0; j < n; j++ )
sum[i] += x[i][j];
}
for ( i = 0; i < m; i++ )
cout << i << “ : ” <<sum [i] << endl;
}
不难看出,嵌套最深层的是第二个for语句那里,根据问题的规模,我们可以知道时间复杂度是T(n)=O(m*n),很简单吧,接下来我们给出几道题目,你们可以算一下时间复杂度之后在评论中给出你们的答案。
好了,最后我们再给出一个常见的时间复杂度顺序表格,方便大家记忆
2.空间复杂度
空间复杂度:算法占用的空间大小。一般将算法的辅助空间作为衡量空间复杂度的标准。空间复杂度的本意是指算法在运行过程中占用了多少存储空间。
算法占用的存储空间包括:
- 输入/输出数据;
- 算法本身;
- 额外需要的辅助空间。
输入/输出数据占用的空间是必需的,算法本身占用的空间可以通过精简算法来缩减,但这个压缩的量是很小的,可以忽略不计。而在运行时使用的辅助变量所占用的空间,即辅助空间是衡量空间复杂度的关键因素。
同样的,我们给出渐进空间复杂度的定义:
—————————————————————————————
若一个算法为递归算法,其空间复杂度为递归所使用的堆栈空间的大小,它等于一次调用所分配的临时存储空间的大小乘以被调用的次数(即为递归调用的次数加1,这个1表示开始进行的一次非递归调用)。算法的空间复杂度一般也以数量级的形式给出。
如当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);
当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为O(log2n);
当一个算法的空间复杂度与n成线性比例关系时,可表示为O(n).
若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;
若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。
总结:算法的时间复杂度和空间复杂度一般是考虑算法效率的两大指标,但是我们不能够单独看某一个指标去评判,因为这两个指标是相互影响的,我们需要根据实际需求去调整这些指标。
以我老师给我说的”六字真言”作为文章的结尾:“01”,“穷举”,“舍得”
如果你还喜欢我的文章,麻烦点一下赞哦~你的好评是我最大的动力!
参考文献:百度百科,《趣学算法》陈小玉著