这篇文章让我们来讨论一下空间复杂度
目录
1.概述
计算机的软硬件都经历了一个比较漫长的演变史,作为为运算提供环境的内存,更是如此,从早些时候的512k,经历了1M,2M,4M...等,发展到现套的8G,甚至16G和32G,所以早期,算法在运行过程中对内存的占用情况也是一个经常需要考虑的问题。我么可以用算法的空间复杂度来描述算法对内存的占用。
2.java中常见内存占用
2.1基本数据类型内存占用情况
2.2计算机访问内存的方式
计算机访问内存是一字节一字节的,即一次访问一字节:
2.3引用大小
一个引用(机器地址)需要8个字节表示
例如:Date date = new Date(); 则这个date变量就需要用8个字节来表示
2.4对象大小
创建一个对象,比如new Date(),除了Date对象内部存储的数据(例如年月日等信息)占用的内存,该对象本身也有内存开销,每个对象的自身开销是16个字节,用来保存对象的头信息。
2.5一般内存占用
一般内存的使用,如果不够8个字节,都会被自动补充为8字节:
例:public class A{
public int a = 1;
}
分析:
通过new A()创建一个对象的内存占用如下:
- 整型成员变量占用4个字节;
- 对象本身占用16个字节
那么创建这个对象总共需要20个字节,但由于不是8的整数倍,所以自动填充至24个字节(实质是int a 时就直接占用8个字节了)
2.6数组占用地址大小
java中数组被被限定为对象,他们一般都会因为记录长度而需要额外的内存,一个原始数据类型的数组一般需要24字节的头信息(16个自己的对象开销,4字节用于保存长度以及4个填充字节)再加上保存值所需的内存
3.算法的空间复杂度
了解了java的内存最基本的机制,就能够有效帮助我们估计大量程序的内存使用情况。
算法的空间复杂度计算公式记作:S(n)=O(f(n));其中n为输入规模,f(n)为语句关于n所占存储空间的函数。
案例:对指定的数组元素进行反转,并返回反转的内容。
解法一:
public static int[] reverse1(int[] arr){
int n = arr.length;//申请4个字节
int temp;申请4个字节
for (int start = 0,end=n-1; start <= end; start++,end--) {
temp=arr[start];
arr[start]=arr[end];
arr[end]=temp;
}
return arr;
}
解法二:
public static int[] reverse2(int[] arr){
int n = arr.length;//申请4个字节
int[] temp = new int[n];//申请4*n个字节+数组自身头信息开销24个字节
for (int i = n-1; i >=0 ; i--) {
temp[n-1-i]=arr[i];
}
return temp;
}
忽略判断条件占用的内存,我们得出的内存占用情况如下:
算法一:
不管传入的数组大小为多少,始终额外申请4+4=8个字节
算法二:
4+4n+24=4n+28
根据大O推导法则,算法一的空间复杂度为O(1),算法二的空间复杂度为O(n),所以从空间占用的角度讲,算法一要优于算法二。由于java中有内存垃圾回收机制,并且jvm对程序的内存占用也有优化(例如即时编译),我们无法精确的评估一个java程序的内存占用情况,但是了解了java的基本内存占用,使我们可以对java程序的内存占用情况进行估算。
由于现在的计算机设备内存一般都比较大,基本上个人计算机都是4G起步,大的可以达到32G,所以内存占用一般情况下并不是我们算法的瓶颈,普通情况下直接说复杂度,默认为算法的时间复杂度。
但是,如果你做的程序是嵌入式开发,尤其是一些传感器设备上的内置程序,由于这些设备的内存很小,一般为几kb,这个时候对算法的空间复杂度就有要求了,但是一般做java开发的,基本上都是服务器开发,一般不存在这样的问题。
4.小结
这篇文章就简要的讲述了一下算法的空间复杂度,其实不是很重要,一些小知识点知道就行,重要的还是时间复杂度。