复杂度分析
算法效率评估
在算法设计中,我们追求以下两个层面的目标。
- 找到问题解法:算法需要再规定的输入范围内可靠地求得问题的正确解
- 寻求最优解法:同一个问题可能存在多种解法,我们希望找到尽可能高效的算法。
也就是说,在能够解决问题的前提下,算法效率已经成为衡量算法优劣的主要评价指标,它包括以下两个度。
- 时间效率:算法运行速度的快慢。
- 空间效率:算法占用内存空间的大小。
复杂度分析能够体现算法运行所需要的时间和空间资源与输入数据大小之间的关系。它描述了随着输入数据的增加,算法执行所需要的时间和空间的增长趋势。
迭代与递归
在算法中,重复执行某个任务是很常见的,它与复杂度分析息息相关。因此,在介绍时间复杂度和空间复杂度之前,先了解如何在程序中实现重复执行任务,即来那个黄总基本的程序控制结构:迭代、递归
迭代
迭代(iteration)是一种重复执行某任务的控制结构。在迭代中,程序会在满足一定的条件下重复执行某代码,知道这个条件不再满足。
1.for循环
for
循环是最常见的迭代形式之一,适合在预先知道迭代次数时使用。
以下函数基于for循环实现了求和1+2+3+...+n,求和结果使用res记录。
int forLoop(int n){
int res = 0;
// 循环求和 1,2,3,4...,n
for (int i = 1; i <= n; i++){
res += i;
}
return res;
}
流程图:
此求和函数的操作与输入数据大小n成正比,或者说成“线性关系”。实际上,时间复杂度描述的就是这个“线性关系”。
2.while循环
与for循环类似,while
循环也是一种实现迭代的方法。在while循环中,每轮都会先检查条件,如果条件为真,则继续执行否则就结束循环。
/*while 循环*/
int whileLoop(int n){
int res = 0;
int i = 1;
while (i <= n){
res += i;
i++;
}
return res;
}
while循环比for循环自由度更高。在while循环中,我们可以自由地设计条件变量的初始化和更新步骤。
3.嵌套循环
我们可以在一个循环结构内嵌套另一个循环结构,下面以for循环为例:
/* 双层 for 循环 */
string nestedForLoop(int n) {
ostringstream res;
// 循环 i = 1, 2, ..., n-1, n
for (int i = 1; i <= n; ++i) {
// 循环 j = 1, 2, ..., n-1, n
for (int j = 1; j <= n; ++j) {
res << "(" << i << ", " << j << "), ";
}
}
return res.str();
}
嵌套循环流程框图:
在这种情况下,函数的操作数量与n2成正比,或者说算法运行时间和输入数据大小n成“平方关系”。我们可以继续添加嵌套循环,每一次嵌套都是一次“升维”,将会使其时间复杂度提高至“立方关系”“四次关系”,以此类推。
递归
递归(recursion)是一种算法策略,通过函数调用自身来解决问题。它主要包含两个阶段。
- 递:程序不断深入地调用自身,通常传入更小或更简化的参数,知道达到“终止条件”。
- 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。
而从实现的角度看,递归代码主要包含三个要素。
- 终止条件:用于决定什么时候由“递”转为“归”。
- 递归调用:对应“递”,函数调用自身,通常输入更小后更简化的参数。
- 返回结果:对应“归”,将当前递归层级的结果返回至上一层。
观察以下代码,我们只需要调用函数recur(n)
就可以完成1+2+3+...+n的计算
/*递归调用*/
int recur(int n){
if (n == 1){
return 1;
}
return n + recur(n - 1);
}
递归过程:
虽然从计算角度看,迭代与递归可以得到相同的结果,但他们代表了两种完全不同的思考和解决问题的范式。
- 迭代:“自下而上”地解决问题。从最基础的步骤开始,然后不断重复或累加这些步骤,直到任务完成。
- 递归:“自上而下”地解决问题。将原问题分解为更小的子问题,这些子问题和原问题具有相同的形式。接下来将子问题继续分解为更小的子问题,直到基本情况时停止(基本情况的解是已知的)
上述求和函数为例,设问题
标签:递归,nums,int,复杂度,算法,循环,数据结构 From: https://www.cnblogs.com/1873cy/p/18366749