一、 数组
定义:数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
特点:支持随机访问,根据下标随机访问的时间复杂度为O(1);插入删除操作的平均时间复杂度为O(n)。
注意点:警惕数组的访问越界问题,在java中,会做越界检查,越界时会报java.lang.ArrayIndexOutOfBoundsException
ArrayList与数组对比:
- ArrayList可以将很多数组操作的细节封装起来(如插入删除操作);
- ArrayList支持动态扩容,存储空间不够时,自动扩容为1.5倍;而数组需指定大小,内存不够时会创建更大的空间,将原数据复制过去在插入新数据
- ArrayList无法存储基本类型,如int类型需要封装Integer类;数组可以
- 当表示多维数组或已知数据大小时,用数组更合适
问题:为什么数组要从0开始计数?
参考回答:
1)从数组存储的内存模型上看,计算a[k]的内存模型公式为:a[k]_address = base_address + k * type_size。当数组从1开始计数,每次随机访问数据元素都对了一次减法运算,为优化性能,故从0开始
2)历史原因:C语言用0开始计数数组下标,效仿c语言
二、链表
1、双向链表对于单链表的优势:(LinkedHashMap容器用到了双向链表)
- 插入删除操作:比如在删除给定指针指向的结点的情况下,双向链表不需要遍历查找前驱结点,可以在O(1)的时间复杂度完成操作。
- 有序链表查询:查询时可以记录上次查找的位置p,每次查询时,根据要查找的值与p的大小关系,决定往前或是往后查找,所以平均只需要查找一半的数据
2、链表与数组性能对比:
1)时间复杂度:
- 数组:插入删除为O(n),随机访问为O(1)
- 链表:插入删除为O(1),随机访问为O(n)
2)访问效率:数组使用的是连续内存,可以借助CPU的缓存机制预读数据,访问效率 更高;链表使用的是零散的内存块,没法有效预读
3)内存空间:由于数组用的是连续内存,当声明的数组过大时,可能导致内存不足(out of memory),声明的数组过小时,又得申请更大空间,在复制插入,费时;链表支持动态扩容,没有大小限制
3、指针理解:将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。
注意点:
1)警惕指针丢失和内存泄漏
如在单链表中,要在结点a和结点b之间插入结点x,要注意操作顺序,即
x->next=p->next;p->next=x;
2)单链表第一个结点插入操作
if(head==null) {
head = new_node;
}
删除链表中的最后一个结点
if(head->next = null) {
Head = null;
}
3)留意边界条件:
链表为空时,能否正常工作;链表只有一个节点时,能否正常工作;链表只有两个结点时能否正常工作;头尾结点处理时,能否正常工作
参考资料:极客时间的《数据结构与算法之美》王争