数组(Array)
一、数组:查询简单,增加和删除困难
数组由相同数据类型的元素按一定顺序排列的集合,在内存中是连续的,数组的空间大小是固定的,占用内存比较大,空间复杂度很大,时间复杂度小。
静态分配内存,存储在栈区,数组的空间是从栈分配的(栈:先进后出)。
二、数组的优点
查找速度快,随机访问性强,可以通过下标快速访问数组元素,时间复杂度是0(1)。因为数组的内存是连续的,想要访问那个元素,直接从数组的首地址向后偏移就可以访问到了。
三、数组的缺点
1、增加和删除困难,插入数据时,待插入位置的元素和它后面的所有元素都需要向后移动;删除数据时,待删除位置后面的所有元素都需要向前移动。
2、数组需要预留空间,在使用前要先申请占内存的大小,可能会浪费内存空间,空间利用率较低;
3、内存空间要求高,必须要有足够的连续的内存空间。
4、数组的空间大小是固定的,不能进行动态扩展。数组开辟的空间,在不够使用的时候需要进行扩容;扩容的话,就需要新建一个数组,把旧数组中的所有元素移动到新数组中。
链表(ListNode)
一、链表:查询相对困难,增加和删除容易
链表由一系列结点组成,链表中每一个元素称为结点,,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 每一个结点都保存了下一个结点的内存地址,通过这个地址找到下一个结点。 上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素。
链表是一种物理存储单元上(内存)非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,链表存储区间离散,占用内存比较宽松,空间复杂度很小,时间复杂度很大。
动态分配内存,存储在堆区,链表的空间是从堆分配的(堆:先进先出、后进后出)。
链表是线性表的一种,所谓的线性表包含顺序线性表和链表,顺序线性表是用数组实现的,在内存中有顺序排列,通过改变数组大小实现。而链表不是用顺序实现的,用指针实现,在内存中不连续。链表就是将一系列不连续的内存联系起来,将那种碎片内存进行合理的利用,解决空间的问题。
二、链表的优点
1、任意位置插入元素和删除元素的速度快,时间复杂度是o(1),插入时,只需要把尾指针指向要插入元素的地址,插入元素的指针指向空值;删除时,只需要把被删除元素前一个结点的指针指向被删除元素后一个结点的地址。
2、内存利用率高,不会浪费内存,可以存在内存中任意地方,空间是分散的,不需要连续内存空间,指针标记了下一个元素的地址。
3、链表的空间不需要提前指定大小,是动态申请的,根据需求动态的申请和删除内存空间,扩展方便,数据可以随意增删。
三、链表的缺点
查找数据时效率低,不具有随机访问性,不能随机查找,必须从第一个开始遍历,根据第一个数据保存的下一个数据的地址找到第二个数据,以此类推,直到找到要查找的数据。
数组 | 链表 | |
---|---|---|
读取(查询) | O(1) | O(n) |
插入 | O(n) | O(1) |
删除 | O(n) | O(1) |
四、链表的类型
1、单向链表
单向链表包含两个域,一个是数据域,一个是指针域。第一部分保存或显示关于结点的信息,第二部分存储下一个结点的地址,最后一个结点的指针指向一个空值。
2、双向链表
双向链表每个结点有2个指针域,一个指针指向前一个结点(当此结点为第一个结点时,指向的是空值或空列表),另一个指针指向后一个结点(当此结点为最后一个结点时,指向的是空值或空列表)。
3、循环链表
循环链表就是首结点和末结点被连接在一起,尾结点的指针指向头结点。循环链表中第一个结点之前就是最后一个结点,反之亦然。