前言
在前面的文章中,我们讲解了时间复杂度。那么衡量算法的效率不仅仅只有时间复杂度,还有空间复杂度。
空间复杂度
含义
空间开销(内存开销)与问题规模n之间的关系。
举例介绍
首先当我们运行一段程序的时候,计算机首先要做的就是讲程序代码读入到内存中。这里我们假设程序代码的大小为100B
有玩过游戏的小伙伴一定都知道,许多的游戏(尤其是比较大型的游戏)在打开的时候需要经历一段加载的操作,其实这就是讲程序代码读入到内存中
但是程序代码的大小是固定的,与问题规模无关。也就是说不管你的问题规模多么庞大, 程序代码就是这些,程序代码不会随着问题规模的变化而变化。
紧接着我们还是使用上篇文章中,的爱你三千遍的代码例子来讲解。
void loveU(int n){ // n为问题规模
int i = 1;
while(i<=n){
printf("I love you %d\n",i);
i++;
}
printf("I love you more than %d\n",n);
}
在上述的代码中,我们需要传入一个int型的变量n,定义一个int型的变量i。于是,当我们运行这段程序的时候,我们还需要开辟一块空间存放这些变量。
由于i和n都是int型的变量,所以我们至少还需要8个B的空间来存储(在这里不考虑一些其他的东西,只是为了更好的理解)在这里n是问题规模,但是不管n为多大,这里的变量存储所占空间都为8B,因为int型就是8B,你最大也只能给到int型最大范围内,不能超出。
于是,这里算法运行所需要的内存空间就为108B,也就是S(n) = O(1)(常数阶)(S代表Space)
原地工作
如果一个算法在内存中所需的空间与问题规模无关(常数阶),那么我们就称该算法可以原地工作。
空间复杂度同样只关心数量级
我们了解了常数阶的空间复杂度,接下来小伙伴就会有疑问那空间复杂度是否和时间复杂度一样只关心数量级呢?
答案是对的,空间复杂度和时间复杂度一样只关心数量级。我们用一段代码来理解。
void test(int n){
int flag[n]; // 声明一个长度为n的数组
int i;
// 省略...
}
上述代码存在一个数组,数组的长度是随着存入的变量多少而改变的。如果存入数组的变量是int型,那么存入n个int型的数值,所占空间就为4n。所以在假设我们存入int型变量的情况下,上述的代码在内存中所占空间应为:4 + 4n + 4 = 4n + 8
- 第一个4是代表n
- 4n是代表数组中存放数据的大小
- 第二个4是代表i
所以上述代码的空间复杂度应为:
- 保留最高阶 -> 4n
- 将系数变为1 -> n
故:空间复杂度为O(n)
我们再通过一段代码的解释,你就能彻底的理解空间复杂度了。
void test(int n){
int flag[n][n];
int i;
// 省略...
}
这里因为n和i都与问题规模无关,所以我们就不需要看n和i,直接看flag。这里flag为一个n×n的二维数组,由此得出该段代码的空间复杂度为O(n2)
加法规则
我们在变量中加入一个一维的数组
void test(int n){
int flag[n][n];
int other[n];
int i;
// 省略...
}
这里的空间复杂度为:S(n) = O(n2) + O(n) + O(1) = O(n2)
空间复杂度的加法规则也是遵循和时间复杂度相同的规则:保留最高阶
常对幂指阶
O ( 1 ) < O ( l