时间复杂度
为什么要有时间复杂度?
第一、在我们的测试环境里面,不同的硬件跑出来的结果是不一样的,比如,i7与i3的机器跑同样的代码花的时间就不一样。
第二、用不同的数据去测试,那么花的时间也是不一样的。
通用公式
所以我们需要一套公式去计算复杂度,通用公式:T = O(N),T表示代码执行的时间,N表示每行代码执行的次数总和,O表示所有代码的执行时间T与每行代码的执行次数总和成正比。(假设每行代码执行的时间是一样的)
O表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度。
开始运用方法:
方法一. 只关注循环执行次数最多的一段代码
int cal(int n) {
int sum = 0;
int i = 1;
for (; i <= n; ++i) {
sum = sum + i;
}
return sum;
}
其中2、3行是常量级别的代码与n的大小无关,所有不用管,执行次数最多的事第4、5行代码,所以我们只管这个两行,时间复杂度是O(n).
方法二、乘法法则,嵌套代码的复杂度等于内外嵌套复杂度的乘积
int cal(int n) {
int ret = 0;
int i = 1;
for (; i < n; ++i) { //第一段 这个函数的复杂度是O(n)
ret = ret + f(i);
}
}
int f(int n) {
int sum = 0;
int i = 1;
for (; i < n; ++i) {//第段段 这函数的复杂度是O(n),合起来就是O(n^2)
sum = sum + i;
}
return sum;
}
这里单看第一段函数的复杂度是O(n),单看第二段函数的复杂度也是O(n),有因为嵌套了,合在一起就是O(n^2)了。
方法三、计算复杂度是总复杂度最多的那段
int cal(int n) {
int sum_1 = 0;
int p = 1;
for (; p < 100; ++p) {//第一段为O(1)
sum_1 = sum_1 + p;
}
int sum_2 = 0;
int q = 1;
for (; q < n; ++q) {//第二段为O(n)
sum_2 = sum_2 + q;
}
int sum_3 = 0;
int i = 1;
int j = 1;
for (; i <= n; ++i) {//第三段为O(n^2)
j = 1;
for (; j <= n; ++j) { //这里又套了一层循环
sum_3 = sum_3 + i * j;
}
}
return sum_1 + sum_2 + sum_3;
}
第一段的执行的次数是常量,所以是O(1),第二段执行了n次所以是O(n),第三段执行了O(n2),所以这段代码最终的时间复杂度为O(n^2)
再看看常见的多项式时间复杂度
第一种:O(1)
int i = 8;
int j = 6;
int sum = i + j;
这种情况,主要没有递归、循环,即使有上千万行的代码,时间复杂度也是O(1)
第二种: O(logn)、O(nlogn)
i=1;
while (i <= n) {
i = i * 2;
}
由上可知道,它的代码执行轨迹就是:2^1 * 2^2 * 2^3 * 2^4 …… 2^x = n 为止,那么求解这个X,就是以2为底的log函数:x=log2n,所以这段代码的时间复杂度是O(log2n),如果在外面再套一层循环m,那么时间复杂度就是O(m*log2n)
第三种:O(m+n)、O(m*n)
int cal(int m, int n) {
int sum_1 = 0;
int i = 1;
for (; i < m; ++i) {//第一段复杂度为O(n)
sum_1 = sum_1 + i;
}
int sum_2 = 0;
int j = 1;
for (; j < n; ++j) {//第二段复杂度为O(m)
sum_2 = sum_2 + j;
}
return sum_1 + sum_2;
}
第一段和第二段的复杂分别是O(n)和O(m),属于同一级别,都是n级别(大于1,小于N的平方),所以cal()方法的复杂度是O(m+n),如果第一段和第二段有嵌套的话,那么可以相乘:O(m*n)
空间复杂度分析
空间复杂度就很简单了,就是一句“占用有多少个变量”就可以解释完毕!
void print(int n) {
int i = 0;
int[] a = new int[n]; // 只有这里占用了n个空间所以空间复杂度是O(n)
for (i; i <n; ++i) {
a[i] = i * i;
}
}
从上面可以看出,只有第3行 new的时候占用了n个空间,其它行占用的空间是O(1),所以答案是O(n)
我们平时能用的空间复杂度一般是是O(1),O(n),O(n^2),其他的O(logn)、O(nlogn)都是用不到的
继续拓展
=========分割线==========
复杂的时间复杂度该怎么算?有下面的问题:
// n表示数组array的长度
int find(int[] array, int n, int x) {
int i = 0;
int pos = -1;
for (; i < n; ++i) {
if (array[i] == x) {
pos = i;
break;
}
}
return pos;
}
这个空间复杂度用上面的方法显示不不行的。
如果数组中第一个元素正好是要查找的变量 x,那就不需要继续遍历剩下的 n-1 个数据了,那时间复杂度就是 O(1)。但如果数组中不存在变量 x,那我们就需要把整个数组都遍历一遍,时间复杂度就成了 O(n)。所以,不同的情况下,这段代码的时间复杂度是不一样的。
这个需要用到概率论来解决,想继续钻研的朋友可以扫码下面的二维码,听听专业老师的讲解。