典型的数据结构之数组和链表

    数组和链表都是典型的线性表,区别在于数组的存储空间上是连续的,而链表的存储空间不一定是连续的,可能是间隔非连续的。看下图就容易理解了。
在这里插入图片描述

数组

数组是一个有限的、类型相同的数据的集合,属于线性表,在内存存储空间中是一段连续的内存区域。

数组有以下特点:

  • 数组中的全部元素是“连续”的存储在一块内存空间中的
  • 数组的下标是从0开始的
  • 数组里面存的数据的类型必须是一致的
  • 数组在定义的时候就需要提前指定固定大小,不能改变。
数组的访问

    数组在访问操作方面有着独特的性能优势,因为数组的元素是连续存储的,通过下标支持随机访问数组中任何一个元素,所以我们可以通过数组内存空间的首地址加上元素的偏移量计算出某一个元素的内存地址,如下:

array[n]的地址 = array数组内存空间的首地址 + 每个元素大小*n

    通过上述公式可知:数组中通过下标去访问数据时并不需要从头开始遍历整个数组,因此数组的访问时间复杂度是 O(1),当然这里需要注意,如果不是通过下标去访问,而是通过内容去查找数组中的元素,则时间复杂度不是O(1),极端的情况下需要遍历整个数组的元素,时间复杂度可能是O(n),当然通过不同的查找算法所需的时间复杂度是不一样的。

数组的插入与删除

    同样是因为数组元素的连续性要求,所以导致数组在插入和删除元素的时候效率比较低。如果要在数组中间插入一个新元素,就必须要将要相邻的后面的元素全部往后移动一个位置,留出空位给这个新元素。如果新元素是插入在数组的最开头位置,那整个原始数组都需要向后移动一位,此时的时间复杂度为最坏情况即O(n),如果新元素要插入的位置是最末尾,则无需其它元素移动,则此时时间复杂度为最好情况即O(1),所以平均而言数组插入的时间复杂度是O(n)。数组的删除与数组的插入是类似的。
    所以整体而言,数组的查询效率高,插入与删除效率低。不过想改善数组的插入与删除效率也是有办法的,来来来,下面的「 链表 」了解一下。

链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,一般用于插入与删除较为频繁的场景。

在这里插入图片描述

    链表的每一个节点通过“指针”链接起来,每一个节点有2部分组成,一部分是数据(上图中的Data),另一部分是后继指针(用来存储后一个节点的地址),在这条链中,最开始的节点称为Head,最末尾节点的指针指向NULL。

链表的访问

    链表的优势并不在与访问,因为链表无法通过首地址和下标去计算出某一个节点的地址,所以链表中如果要查找某个节点,则需要一个节点一个节点的遍历,因此链表的访问时间复杂度为O(n)。

链表的插入与删除

    也正式因为链表内存空间是非连续的,所以它对元素的插入和删除时,并不需要像数组那样移动其它元素,只需要修改指针的指向即可。
在这里插入图片描述
如上图所示,在要在b和c节点中间插入x节点:

  1. 删除b节点指向c节点的指针地址
  2. 把b节点指针地址指向x节点
  3. 把b节点指针地址再指向c节点

    删除某个节点类似,只需要改动指针指向,不需要移动数据。从上例子可以看出链表的时间插入删除的时间复杂度为O(1),不过注意下这里指的是找到节点之后(查找节点也需要时间的)纯粹的插入或删除动作所需的时间复杂度。如果当前还未定位到指定的节点,只是拿到链表的Head节点,这个时候要去删除此链表中某个固定内容的节点,则需要先查找到那个节点,这个查找的动作又是一个遍历动作了,这个遍历查找的时间复杂度却是O(n),两者加起来总的时间复杂度其实是O(n)的。其实就算是已经定位到了某个要删除的节点了,删除逻辑也不简单。以“删除上图的b节点”为例,假如当前链表指针已经定位到了b节点,删除的时候,需要将这个b节点的前面一个节点a的后继指针改为指向c节点,那么b节点就会自动脱落了,但是当前链表指针是定位在b节点上,如何去改变a节点的后续指针呢?对于“单向链表”而言,这个时候需要从头遍历一遍整个链表,找到a节点然后修改其后继指针的内容,所以时间复杂度是O(n),但如果当前是“双向链表”,则不需要遍历,直接通过b节点的前继指针即可找到a节点,时间复杂度是O(1),这里就是“双向链表”相当于“单向链表”的优势所在。所以一般情况下单链表常用于首尾插入和删除操作,免去中间查找节点的时间。
    当然,上面讲的只是链表中最简单的一种单向链表,它的每一个节点只有一个指针(后继指针)指向后面一个节点,除此之外还有双向链表、循环链表等。

循环链表

    其实循环链表就是一种特殊的单向链表,只不过在单向链表的基础上,将尾节点的指针指向了Head节点,使之首尾相连。循环链表的尾指针不是指向NULL而是指向头结点,它有什么用那?就是可以限制使用的内存空间的大小,用来做缓存还是蛮好的。
在这里插入图片描述

双向链表

在这里插入图片描述
    双向链表与单向链表的区别是前者是2个方向都有指针,后者只有1个方向的指针。双向链表的每一个节点都有2个指针,一个指向前节点,一个指向后节点。双向链表在操作的时候比单向链表的效率要高很多,但是由于多一个指针空间,所以占用内存也会多一点。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值