二、基础篇
推荐一个课程:数据结构与算法之美(极客时间)
1. 数组
1. 为什么很多编程语言的数组都是从0开始编号的?
- 从数组存储的内存模型上来看,“下标”确切的说法就是一种“偏移”,相比从1开始编号,从0开始编号会少一次减法运算,数组作为非常基础的数组结构,通过下标随机访问元素又是非常基础的操作,效率的优化就要尽可能的做到极致。
- 主要的原因是历史原因,C语言的设计者是从0开始计数数组下标的,之后的Java、JS等语言都进行了效仿,或者说是为了减少从C转向Java、JS等的学习成本。
2. 什么是数组?
数组是一个线性数据结构,用一组连续的内存空间存储一组具有相同类型的数据。其实数组、链表、栈、队列都是线性表结构;树、图则是非线性表结构。
3. 数组和链表的面试纠错?
- 数组中的元素存在一个连续的内存空间中,而链表中的元素可以不存在于连续的内存空间。
- 数组支持随机访问,根据下标随机访问的时间复杂度是O(1);链表适合插入、删除操作,时间复杂度为O(1)。
4. 容器是否完全替代数组?
容器的优势:对于Java语言,容器封装了数组插入、删除等操作的细节,并且支持动态扩容。对于Java,一些更适合用数组的场景:
- Java的ArrayList无法存储基本类型,需要进行装箱操作,而装箱与拆箱操作都会有一定的性能消耗,如果特别注意性能,或者希望使用基本类型,就可以选用数组。
- 若数组大小事先已知,并且对数组只有非常简单的操作,不需要使用到ArrayList提供的大部分方法,则可以直接使用数组。
- 多维数组时,使用数组会更加直观。
5. 补充:
-
对于数组的操作:
-
插入:如果我们要在第 k 个位置的数据,为了内存的连续性,需要搬移k 位置当前以及之后的数据往后移动一格。如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的集合。在这种情况下,如果要将某个数据插入到第 k 个位置,为了避免大规模的数据搬移,我们还有一个简单的办法就是,直接将第 k 位的数据搬移到数组元素的最后,把新的元素直接放入第 k 个位置。
假设数组 a[10]中存储了如下 5 个元素:a,b,c,d,e。我们现在需要将元素 x 插入到第 3 个位置。我们只需要将 c 放入到 a[5],将 a[2]赋值为 x 即可。最后,数组中的元素如下: a,b,x,d,e,c。
-
删除:如果我们要删除第 k 个位置的数据,为了内存的连续性,也需要搬移数据。在某些特殊场景下,我们并不一定非得追求数组中数据的连续性。如果我们将多次删除操作集中在一起执行,删除的效率是不是会提高很多呢?如果你了解过JVM,是不是觉得这就是 JVM 标记清除垃圾回收算法的核心思想吗
-
-
标记清除算法:GC最基础的收集算法就是标记-清除算法,如同他们的名字一样,此算法分为“标记”、“清除”两个阶段,先标记出需要回收的对象,再统一回收标记的对象。不足有二,1.标记和清理效率都不高,但是当知道只有少量垃圾产生时会很高效。2.空间问题。会产生不连续的内存空间碎片。
-
标记-整理垃圾回收算法:在标记完成之后让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
数组的内存寻址公式?
一维数组:a[i]_address=base_address+itype_size
二维数组:二维数组假设是mn, a[i][j]_address=base_address + (in+j)type_size
三维数组:三维数组假设是mnq, a[i][j][k]_address=base_address + (inq + j*q + k)*type_size注意:java二维数组是分块连续的