算法基础:基本数据结构的特点与复杂度:数组 vs 链表

在这里插入图片描述
这篇文章介绍一下基本数据结构中数组和链表的特点和复杂度。

数组(Array)

特点

数组是最为常见的一种数据结构,用于保存有限个变量所组成的有序集合,主要特点如下:

  • 数据元素通过下标进行访问,读取方式称为随机读取,长度为n的可访问下标为0至n-1,此范围之外的下标访问时均会出现下标越界的错误
  • 内存中进行顺序存储,一般使用连续的内存单元
  • 一般能保存的变量的个数受限,数组一般会指定长度,一般为定长数组,扩容操作一般是申请更大的空间,扩容时同时会将之前的数据拷贝至新数组中。

操作与时间复杂度

读取元素(查)

数组通过下标进行访问的随机读取,定位方式非常高效,通过下标能够在常量时间内进行读操作,时间复杂度为O(1),基于数组实现的二分查找也使用了这个特性。

更新元素(改)

更新操作和读操作一样,数组通过下标进行随机访问,定位方式非常高效,通过下标能够在常量时间内进行更新操作,时间复杂度为O(1)。

添加元素(增)

添加元素又称为插入元素,向数组中添加数据结构中可能会存在如下三种情况:

  • 尾部插入元素:在数组存储空间未全部使用完毕的情况下,向数组的尾部添加新的元素不会产生数据的移动,直接插入即可。
  • 中间插入元素:在数组最后一个元素之前的任何位置插入都会产生数组元素的移动,将插入点之后的所有数据元素均逐次后移,在空出的位置进行新元素的插入
  • 数组扩容:向数组的尾部或者中间插入新的数据,如果数组已满,需要数组扩容之后再进行添加

添加元素如果在尾部插入又无需扩容的情况下,时间复杂度为O(1),考虑到多种情况,平均意义上的插入时间复杂度为O(n)

删除元素(删)

删除元素同样也存在尾部删除或者中间删除的情况

  • 删除尾部元素:删除数组的尾部元素不会产生数据的移动,直接删除即可。
  • 删除中间元素:一般的做法来说,删除数组最后一个元素之前的任何数组元素都需要进行数据的移动,将删除元素产生的空位使用其后的元素逐次前移(在数组对顺序不做要求的情况下,也可以直接将最后一个数据移至删除元素位置)

删除元素如果不使用将最后一个元素直接填入删除元素所在的位置的情况下,由删除数组元素所产生的元素移动的平均时间复杂度为O(n)

链表(Linked List)

特点

相较于数组,链表也是一种非常常见的数据结构,链表的特点主要如下所示:

  • 物理存储上非连续,数组为顺序存储,需要大段空间,而链表则体现为随机存储,可见缝插针地使用空间
  • 一般分为双向链表或者单向链表
  • 链表的元素一般称为节点,节点由存放数据的部分和存放链接信息的指针数据组成
  • 单向链表一般保存next指针关联后续节点,双向链表还需要previous指针指向前面的节点

操作与时间复杂度

整体来说,相较于数组的随机访问,无论是双向链表还是单向链表,都需要通过指针不断的移动来定位数据元素,在读取上效率较低,但在修改上较为高效

读取元素(查)

需要按照顺序进行元素的访问,访问长度为n的链表的最后一个元素,为最坏的时间复杂度,整体来说,平均时间复杂度为O(n)

更新元素(改)

不考虑读取元素所需要的成本,更新自身非常简单,直接更新就可以了,时间复杂度为O(1)

添加元素(增)

不考虑读取元素所需要的成本,删除自身非常简单,无非是头部尾部单向双线的组合时链接信息的前面和后面的节点是否存在进行考虑一下即可,因为是链式结构,直接修改链接的信息后插入数据即可,时间复杂度为常数,即O(1)

删除元素(删)

同样,和添加一样,删除元素节点时如果不需要考虑读取元素的过程,删除自身的时间复杂度也是O(1)

数组 vs 链表

时间复杂度比较

类型 读取元素(查) 更新元素(改) 添加元素(增) 删除元素(删)
数组 O(1) O(1) O(n) O(n)
链表 O(n) O(1) O(1) O(1)

使用场景

从时间复杂度的比较上就可以看出,两者的优势和劣势场景不同,简单为:

  • 数组:查询操作为主,更新操作较少
  • 连标:更新操作较为频繁

你可能感兴趣的:(算法基础,数组,链表,算法基础,时间复杂度)