一、为什么要分析复杂度
1、假设我们每次都直接用机器去运行代码来测试代码运行花费的时间,测试的结果很受硬件的影响,比如说i9的CPU和i3的测试结果肯定有很大的差距
2、用机器去测试代码的效率很受数据规模的影响,举个极端的例子,几十个数据排序,插入排序可能比快排要快。
所以我们要用一个不用具体的测试数据也不依赖于硬件条件的测试方法,来粗略的估算出算法(代码)的执行效率
二、如何表示复杂度?
T ( n ) = O ( f ( n ) ) T(n) = O(f(n)) T(n)=O(f(n))
T(n)表示代码执行的时间,n表示数据规模或者数据量,公式f(n)表示代码执行的次数,O表示T(n)和f(n)成正比。
三、时间复杂度是什么?如何分析时间复杂度?
时间复杂度,也叫渐进时间复杂度,表示算法的执行时间和数据规模之间的关系
分析时间复杂度的方法有三种
1、只关注循环次数最多的一段代码
我们在分析一段代码的时候,只关注循环执行次数最多的那一段代码就可以了
int cal(int n ){
int sum = 0;
int i = 1;
for(;i<=n;++i){
sum = sum + i;
}
return sum;
}
上述代码中,2、3行代码执行1次,是常量级,与数据量的多少(n的大小)无关系,可以忽略。4、5行代码都被执行了n次,所以总的时间复杂度是O(n)。
2、加法法则:总复杂度等于量级最大的那段代码的复杂度
int cal(int n ){
int sum_1 = 0;
int p = 1;
for(;p<100;++p){
sum_1 = sum_1 + p;
}
int sum_2 = 0;
int q = 1;
for(;q<n;++q){
sum_2 = sum_2 + q;
}
int sum_3 = 0;
int i = 1;
int j = 1;
for(;i<=n;++i){
j = 1;
for(;j<=n;++j){
sum_3 = sum_3 + i + j;
}
}
return sum_1 + sum_2 + sum_3;
}
上述这段代码分别求sum_1、sum_2、sum_3,最后求它们总和。我们分成三部分,分别求时间复杂度。
第一部分,只执行了100次,我们算O(100)。
第二部分,执行了n次,时间复杂度是O(n)。
第三部分,执行了外层循环的n次,内层循环的n²次,时间复杂度我们算O(n+n²)。
我们把三部分加起来就是O(100 + n + n + n²) = O(100 + 2n + n²),我们去掉常数,低阶,系数,最终时间复杂度为O(n²)。
去掉常数,低阶,常量系数的原因是因为他们随着数据规模的增大,影响很小。
其中常数(比如刚才的100次),无论是一百万个数据还是10个数据,都执行一百次,所以直接省略。
系数省略(比如2n的2),无论n的值为多少,他都是常量2,所以省略。
低阶省略(省略n),因为相比于n²来说,n的影响远远小于n²。
所以我们取量级最大的复杂度来表示
3、乘法法则:嵌套代码的复杂度等于嵌套内外复杂度的乘积
int f(int n )
{
int sum = 0;
int i =1;
for(;i<n;++i)
{
sum = sum + i;
}
return sum;
}
int cal(int n ){
int ret = 0;
int i = 1;
for(;i<=n;++i){
ret = ret + f(i);
}
return sum;
}
可以看到函数cal是一个单层循环,但是每次循环都会调用函数f;所以整个时间复杂度是T(n) = O(n * n) = O(n²)。
四、时间复杂度分类
时间复杂度分为多项式量级和非多项式量级如下。
多项式量级:O(1), O(log n ), O(n), O(nlog n), O(n²)
非多项式量级:O(2的n次方), O(n!)
(算法的时间复杂度如果是非多项式量级,就是很差了)
五、空间复杂度是什么?如何分析空间复杂度?
空间复杂度也叫渐进空间复杂度,表示算法的储存空间与数据规模的增长关系
分析方式很简单,举个例子
void print(int n )
{
int i = 0;
int[] a = new int[n];
for(;i<n;++i)
{
a[i] = i * i;
}
for(i = n-1;i>=0;--i)
{
cout<<a[i]<<endl;
}
}
整段代码申请了n个int的空间,所以空间复杂度为O(n)。