时间复杂度:CPU读写一次内存算作时间复杂度的最小单位。
读内存的场景:获取变量的值。
例如: if(x < 1000)
写内存的场景:给变量赋值。
例如:x = 1000
空间复杂度:内存占用一个字节作为空间复杂度的最小单位。
如上图所示,由于CPU运算(加减乘除)特别快,而内存的速度与之相比慢了很多,但是CPU又只有有限的几个寄存器可以参与计算,所以,数据总是保存在内存中,通过内存数据反复的和寄存器交换数据,再由CPU加工寄存器数据来完成实际的运算,这就导致瓶颈发生在内存的读写上。
如果一个计算过程需要更多的内存字节数量,我们就说这个计算过程的空间复杂度比较高。
如果一个计算过程需要更多的内存读写次数,我们就说这个计算过程的时间复杂度比较高。
如何量化时间复杂度和空间复杂度呢?
我们用时间复杂度和空间复杂度的最小单位的函数来表示。
比如,我想求 n 个整数的最大值。
空间复杂度怎么量化?
那么,我就需要 n 个内存空间来存储这 n 个整数。这时候我们就说求 n 个整数最大值的过程(算法),需要的空间复杂度为 O(n)。
时间复杂度怎么量化?
假设,这 n 个整数 连续的存储在一个数组中,那么需要的时间复杂度就是需要遍历这个 n 个整数。代码如下:
int max_num = array[0];
for(int i = 0; i < n; ++i)
{
max_num = max(max_num, array[i]);
}
return max_num;
从代码中可以看出来,我们需要读取内存 次数为 1 + n * 3。
其中, 1 对应: int max_num = array[0]; 也就是给 max_num 初始化。
其中, n * 3 对应: for(i : 0 ~ n-1) max_num = max(max_num, array[i])。 循环执行了[0~n-1] 次,也就是 n 次。
循环的每一次,访问了几次内存呢? 3 次。
为何是 3 次呢? 其实是 max_num = max(max_num, array[i]); 这条语句访问了 3 次内存。
读取 max_num 原来的值 1 次(读内存), 读取 array[i] 的值 1 次(读内存), 最后把 新的 max 求出来(这个比较是在CPU内部寄存器完成的,没有再发生内存读取),求出了 新的 max 之后 写回到内存覆盖 max_num 之前的值。 这时候 是 1 次写内存。
所以,总共是 2 次读内存, 1 次 写内存。 总共 是 3 次。
综上,求 n 个数的最大值,时间复杂度为 1 + n * 3。
由于随着 n 的规模变大, 常数 1 可以忽略。 比如, 1000000000 个整数的最大值,和 1000000001个整数的最大值,我们认为时间复杂度是相同的。
这样时间复杂度 就变成了 O(n*3) 。
另外,常数项系数,并没有从本质上改变复杂度。我们通常把常数项都改写为 1 来简化问题复杂度的描述。
这样以来,时间复杂度就变成了 O(n)。