3.0、C语言数据结构——时间复杂度和空间复杂度(2)
算法的时间复杂度:
算法时间复杂度的定义:在进行算法分析时,语句总是执行次数 T(n) 是关于问题规模 n 的函数,进而分析 T(n) 随 n 的变化情况并确定 T(n) 的数量级。算法的时间复杂度也就是算法的时间量度,基座 T(n) = O(f(n)) 他表示随问题规模 n 的增大,算法执行时间的增长率和 f(n) 的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中 f(n) 是问题规模 n 的某个函数
这样用大写O()来体现算法时间复杂度的记法,我们称为大O记法;
一般情况下,随着输入规模 n 的增大,T(n)增长最慢的算法为最优算法
显然,由此算法时间复杂度的定义可知,我们的三个求和算法的时间复杂度分别为 O(1),O(n)、O(n^2)
那么如何分析一个算法的时间复杂度呢?即如何推导大 O 阶呢?给大家整理了以下攻略:
1.用常数 1 取代运行时间中的所有加法常数
2.在修改后的运行次数函数中,只保留最高阶项
3.如果最高阶存在且不是 1 的常数,则去除这个项相乘的常数
给大家举几个例子:
int main() {
int sum = o;
int n = 100;
printf("澜色海湾~");
printf("澜色海湾~");
printf("澜色海湾~");
printf("澜色海湾~");
printf("澜色海湾~");
printf("澜色海湾~");
sum = (1 + n)*n/2;
return 0;
}
O(9)?这是初学者常常犯的错误,总认为有多少条语句就有多少...
分析下,按照我们的概念“T(n) 是关于问题规模 n 的函数”来说,这跟问题的规模没有半点关系,所以我们记作 O(1) 接可以了
再来看一段代码【线性阶】:
int main() {
int i = 0;
int n = 100;
int sum = 0;
for(i = 0;i < n;i++) {
sum = sum + 1;
}
return 0;
}
上面这段代码,他的循环时间复杂度为 O(n) ,因为循环体中的代码需要执行 n 次
再来看一段代码【平方阶】:
int main() {
int i = 0;
int j = 0;
int n = 100;
for(i = 0;i < n;i++) {
for(j = 0;j < n;j++) {
printf("澜色海湾");
}
}
return 0;
}
n 等于 100,也就是说外层循环每执行一次,内存循环就执行100次,那总共程序想要从这个两个循环出来,需要执行 100 * 100 次,也就是n的平方。所以这段代码的时间复杂度为 O(n^2);
那如果有三个这样的嵌套循环呢?
没错,那就是 n^3 啦,所以我们很容易总结得出,循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数
但是当我们的 j = i 的时候就不这么简单了 ->
int main() {
int i = 0;
int j = 0;
int n = 100;
for(i = 0;i < n;i++) {
for(j = i;j < n;j++) {
printf("澜色海湾");
}
}
return 0;
}
分析下,由于当 i = 0时,内循环执行了 n 次,当 i = 1 时,内循环则执行了 n-1 次......当 i = n - 1 时,内循环执行 1 次,所以总的执行次数应该是:n(n + 1) / 2 【这里用到了等差数列的计算方法 -> (首项 + 尾项) * 项数 / 2】;
那咱们理解后可以继续化简得到 n (n + 1) / 2 = n^2 / 2+n / 2 ;
用我们推导大O的攻略,第一条忽略 -> 因为没有常数相加;第二条只保留最高项 -> 所以n / 2 这项去掉;第三条 -> 去除与最高项相乘的常数;那么最终我们可以得到时间是复杂度为 -> O(n^2)
对比各种例子的时间复杂度 ->
常用的时间复杂度所耗费的时间从小到大依次是:
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
空间复杂度:
我们在写代码时,完全可以用 空间 来换取 时间
举个例子,要判断某年是不是闰年 ->
第一种方法 -> 你可能会花一点心思来写一个算法,每给一个年份,就可以通过这个算法计算得到是否为闰年的结果;
第二种方法 -> 实现建立一个有2050个元素的数组,然后把所有的年份下标的数字对应,如果是闰年,则此数组元素的值是 1 ,如果不是则元素的值为 0。这样所谓的判断某一年是否为闰年就变成了查找这个数组某一个元素的值的问题
第一种方法 -> 相比起第二种来说很明显非常非常节省空间,但每一次查询都需要经过一系列的计算才能知道是否为闰年;
第二种方法 -> 虽然需要在内存里存储2050个元素的数组,但是每次查询只需要一次索引判断即可
就是通过一笔空间上的开销来换取计算时间开销的技巧,那么到底哪一种方法更好呢?其实还是要看你用在什么地方了
算法的空间复杂度通过计算算法所需要的存储空间实现,算法的空间复杂度的计算公式记作:S(n) = O ( f ( n ) ) ,其中,n为问题的规模,f ( n ) 为语句关于 n 所占存储空间的函数;
通常,我们都是用 “ 时间复杂度 ” 来指运行时间的需求,用 “ 空间复杂度 ” 指空间需求;
当直接要让我们求 “ 复杂度 ”时,通常指的是时间复杂度;
显然对时间复杂度的追求更是属于算法的潮流~