数组特性:
1.线性表(包括数组、链表、队列和栈等)
2.连续内存空间和相同类型数据。
基于特性2:连续的内存空间和相同类型数据:
1.让数组具有随机访问特性。
2.为保证连续性,插入和删除数据可能会做大量数据搬移工作。
理解:随机访问特性(根据下标进行访问)
1.基于公式:a[i]_address=base_address + i*data_type_size。所以时间复杂度为O(1)。
2.注意:查找某个元素的时间复杂度不是O(1),对于排好序的数组二分查找(折半查找)时间复杂度也是O(logn)。
理解:插入操作(注意思考分配内存不足的问题)
1.尾部插入时间复杂度是O(1)(最好情况时间复杂度)。
2.头部插入时间复杂度为O(n)(最坏情况时间复杂度)。
3.平均时间复杂度(1+2+3+...+n)/n=O(n)。
4.优化方式:
不需要保证稳定性的情况下将插入位置的数据放在最后,然后再插入数据。
对于连续插入可以一次性插入。
理解:删除操作
1.尾部删除时间复杂度O(1)(最好)。
2.头部删除时间复杂度O(n)(最坏)。
3.优化方式:
特殊场景下,不一定追求数据连续性,可以将多次删除集中为一次操作,减少数据搬移。类比java中JVM标记-清除算法的思想。
警惕数组越界
int main(int argc, char* argv[]){
int i = 0;
int arr[3] = {0};
for(; i<=3; i++){
arr[i] = 0;
printf("hello world\n");
}
return 0;
}
这段c代码中,由于人为失误:导致数组越界,数组越界造成了无限循环。对于不同编译器,在分配内存时,会按照内存地址递增或递减的方式进行分配。上面按照递减方式进行会出现无限循环。
在 C 语言中,只要不是访问受限的内存,所有的内存空间都是可以自由访问的。根据我们前面讲的数组寻址公式,a[3] 也会被定位到某块不属于数组的内存地址上,而这个地址正好是存储变量 i 的内存地址,那么 a[3]=0 就相当于 i=0,所以就会导致代码无限循环。
说说数组和链表
1.数组对内存要求较高,需要连续。
2.链表需要额外的空间存指定地址。
3.数组由于连续内存空间和相同类型数据具有随机访问特性(n(1))。
4.链表更适合插入删除。在无法保证连续的大的内存空间的时候适合使用。性能更好。
Java中数组和容器ArrayList各自优缺点对比
1.ArrayList封装了数组操作中的细节,如:在插入删除过程中数据的搬移。
2.ArrayList支持动态扩容。这些细节不需要我们实现。注意:扩容涉及内存申请和数据搬移,比较耗时,建议创建ArrayList的时候就指定数据大小。
3.数组中如果需要更大的空间,将原来的数据复制进去,然后再将新的数据插入进行。这些需要我们来做。
4.ArrayList无法存储基本类型,需要基础类型的包装类,拆箱装箱需要一定性能消耗。希望使用基本数据类型就用数组。
5.针对二维数组或多维数组,Object[][]array比ArrayList<ArrayList>array更直观。
总结:对于业务开发用容器省时省力,损失性能不大,对底层开发对性能要求高的数组优于容器。
数组相关code:https://github.com/wangzheng0822/algo/tree/master/java/05_array
数据结构和算法,你如果不接触可能以后都不会接触,但是你接触了,你就会感觉到它的好。
数据结构和算法建议大家可以看看这个专栏老师写的很好,很感谢他:http://gk.link/a/103WJ