算法基本知识1------数组

    xiaoxiao2022-07-05  136

    1.定义

    1.数组是一种***线性表***(只有前后位置关系)数据结构; 2.他用一组***连续的内存空间***存储一组***相同类型的数据***

    2.数组和链表的区别

    链表适合插入和删除,时间复杂度是O(1); 数组适合***随机访问***,根据下标随机访问的时间复杂度是O(1)

    3.数组数据删除的优化

    若删除末尾元素,没有数据的移动,时间复杂度O(1); 若删除开头元素,删除之后,所有的数据都要向前移动一位,时间复杂度是O(n); 平均情况时间复杂度是O(n);

    优化

    若要删除多个元素,为了避免其他数据被移动多次,我们可以记录下已经删除的数据,每次的删除操作并不是真正的移除数据,只是记录某个数据已经被删除,当数组中没有更多空间来储存数据时,我们才触发一次真正的删除操作。

    4.数组数据插入的优化

    若在末尾插入元素,没有数据的移动,时间复杂度O(1); 若在开头插入元素,所有的数据都要向后移动一位,然后插入新的数据,时间复杂度是O(n); 平均情况时间复杂度是O(n);

    优化

    如果想要在k位置插入一个新的元素,我们可以直接将k位置的元素移动到数组末尾,然后在k位置插入新的数据

    5.数组越界问题

    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; } 运行结果是:无限的打印

    原因: 数组大小为3,a[0] a[1] a[2]; 当i=3时 数组a[3]访问越界,但是a[3]也会被定位到某块不属于数组的内存地址上; 如果这个内存地址恰好是存储变量i的内存地址,那么a[3]=0也就是i=0,会导致代码无限的循环。 (java会做越界检查)

    6.数组和容器的使用场景

    容器的优势:可以将很多数组操作的细节封装起来;支持动态扩容(扩容操作涉及到内存的申请和数据的搬移,比较耗时,最好在创建容器的时候就指定好数据的大小)。

    什么情况下用数组不用容器: 1.如果使用基本类型,可以使用数组(容器无法存储基本类型,比如int,long,需要封装为Integer和Long,java虽然有自动拆箱和装箱机制,但是有性能的消耗) 2.如果数据大小已知,并且对数据的操作非常简单,涉及不到容器提供的大部分方法,可以用数组 3.表示多维数组时,数组比较直观

    7.数组为什么从0开始

    a[i] i下标最确切的定义应该是“偏移” i如果是从0开始,a[i]的内存地址是:a[i]=数组首地址+i*type_size(存储数据类型的尺寸 例如 int为4字节) i如果是从1开始,a[i]的内存地址是:a[i]=数组首地址+(i-1)*type_size,每次随机访问数组元素多了一次减法运算,对于cpu来说是一种负担。

    历史原因:其他语言都效仿c语言从0开始

    8.jvm的标记清除垃圾回收算法

    垃圾收集算法第一种:标记-清除算法

    第一步:标记出所有需要回收的对象(类比删除:记录所有删除的数据) 第二步:在标记完成后统一回收被标记的对象(类比删除:数组没有多余内存存储数据时,全部清除)

    缺点:

    1.标记和清除效率都很低(时间问题); 2.会产生很多不连续的内存碎片(空间问题),导致以后需要较大内存时,无法找到足够的连续空间而不得不提前触发垃圾收集动作

    垃圾收集算法第二种:复制算法(解决时间问题)

    第一步:将可用内存按照容量划分为相等的两块; 第二步:每次只使用其中的一块A; 第三步:当使用的一块内存A用完了,就将还存活的对象复制到另外一块内存B上面; 第四步:原来使用的内存空间A一次清理掉。

    缺点:

    1.内存缩小了一半

    应用场景:

    回收新生代(只浪费了百分之十的内存): 第一步:将内存划分为Eden空间和两块较小的Survivor空间,HotSpot虚拟机是8:1; 第二步:每次只使用Eden空间和一块Survivor空间(A); 第三步:当使用的内存用完时,把所有存活着的对象复制到另外一块Survivor空间(B); 第四步:原来使用的内存空间A一次全部清理掉。

    在第三步中,如果B空间不够用,需要依赖其他的内存(这里指老年代)进行分配担保

    缺点:

    1.当对象存活率较高的时候就需要进行较多的复制操作,效率将会变低 2.还要有额外的空间作为担保

    垃圾收集算法第三种:标记-整理算法

    第一步:标记出所有需要回收的对象; 第二步:将所有的存活对象都向一端移动; 第三步:直接清理掉端边界以外的内存。

    垃圾收集算法第四种:分代收集算法(对复制算法的优化)

    第一步:将Java堆分为新生代和老生代(根据对象存活周期的不同); 第二步:新生代中采用复制算法(新生代中对象存活率较低); 第三步:老生代中采用标记-清除或者标记-整理算法(老生代中对象存活率比较高,没有额外的空间对她进行分配担保)

    9.一维数组的内存寻址公式,二维数组的内存寻址公式

    一维数组:a[i]=数组首地址+i*type_size(存储数据类型的尺寸 例如 int为4字节)

    二维数组:如果是a[m][n]:是m个1维数组(数组元素为n个) a【i】【j】=数组首地址+(i*n+j)*type_size

    最新回复(0)