首先我们要知道,算法的时间复杂度是在算法运行之前,预估算法时间开销T(n)与问题规模n的关系(T表示time),分析一个算法的时间复杂度的时候,最重要的就是同n来表示出时间开销T。接下来我们用一个小例子来进行理解:
在《复仇者联盟4》中的一个片段,父亲对女儿说“我爱你1000遍”,女儿回答说“我爱你3000遍”那么我们来设计一个算法来实现“表白爱你n遍”
#include <stdio.h>
void loveyou(int n){//n为问题规模
int i=1;
while(i<=n)
{
i++;//每次+1
printf("I love you %d\n",i);
}
printf("I love you more than %d\n");
}
int main()
{
loveyou(3000);
return 0;
}
我们来分析一下,其中的代码分别运行来几次
在上面的例子中,时间复杂度的公式还是相对简单的,但是对于不同的情况,公式越发复杂时,我们无法通过公式对其复杂度进行判断,接下来我们来思考:
问题1:是否可以忽略表达式中的某些部分?
问题2:如果有好几千行代码,按这种方法需要一行一行数?
通过以上问题的思考,我们得到以下结论:
在问题1中:
当n足够大时,我们可以只考虑阶数高的部分,用O(f(n))表示,O的含义是T(n)的数量级,f(n)表示执行次数,其余部分忽略,公式如下:
接下来我们看乘法中的举例,该例子中,相加两项哪一个阶数更大呢?
需要用到以下公式:
通过以下的函数表达式还能够更直观的看出其对应关系,我们可以用“常对幂指阶”这样的口诀进行记忆
在问题2中:
1.顺序执行的代码只会影响常数项,可以忽略。
2.只需要挑循环中的一个基本操作分析它的执行次数与n的关系即可
3.当有嵌套循环时,比如内层循环执行n的2次方次,外层循环执行n次,我们只需要保留最高阶的部分即可,即只需要关注最深层循环循环了几次
接下来我们来做一下相关练习:
练习1:
#include <stdio.h>
void loveyou(int n){//n为问题规模
int i=1;
while(i<=n)
{
i=i*2;//每次翻倍
printf("I love you %d\n",i);
}
printf("I love you more than %d\n");
}
int main()
{
loveyou(3000);
return 0;
}
练习2:
#include<stdio.h>
void loveyou(int flag[], int n)
{
for (int i = 2; i < n;i++)
{
if (flag[i]==n)
{
printf("第%d次找到",i+1);
break;
}
}
}
int main()
{
int flag[5] = { 1,4,3,5,2 };
loveyou(flag, 5);
return 0;
}
我们将上面的代码展开分析,在定义的函数中定义了一个数组和n,在数组中,n的位置不确定,我们来查找n的位置,有三种情况,截图中已给出,在这三种情况下我们来计算时间复杂度,通过这个题目我们需要知道的是,在n不确定的情况下,我们要将其分开讨论。(代码给出的样例令n=5进行运行)
知识汇总:
空间复杂度考察相对较少也相对简单,这里将知识框架汇总如下: