数据结构_线性表

线性表

1.什么是线性表

线性表是 n 个数据元素的有限序列,最常用的是链式表达,通常也叫作线性链表或者链表。在链表中存储的数据元素也叫作结点,一个结点存储的就是一条数据记录。每个结点的结构包括两个部分:

  • 第一是具体的数据值;

  • 第二是指向下一个结点的指针。

在这里插入图片描述
在这里插入图片描述
仔细观察上图,你会发现这个链表只能通过上一个结点的指针找到下一个结点,反过来则是行不通的。因此,这样的链表也被称作单向链表。
有时候为了弥补单向链表的不足,我们可以对结点的结构进行改造:

对于一个单向链表,让最后一个元素的指针指向第一个元素,就得到了循环链表;
在这里插入图片描述

或者把结点的结构进行改造,除了有指向下一个结点的指针以外,再增加一个指向上一个结点的指针。这样就得到了双向链表。
在这里插入图片描述
同样的,还可以对双向链表和循环链表进行融合,就得到了双向循环链表,如下图所示:
在这里插入图片描述

2.线性表的增删查

我们主要介绍单向链表的增删查操作
新增
在这里插入图片描述

s.next = p.next;
p.next = s;

删除
在这里插入图片描述

p.next = p.next.next;


只能遍历查询

总结
链表在新增、删除数据都比较容易,可以在 O(1) 的时间复杂度内完成。但对于查找,不管是按照位置的查找还是按照数值条件的查找,都需要对全部数据进行遍历。这显然就是 O(n) 的时间复杂度。

虽然链表在新增和删除数据上有优势,但仔细思考就会发现,这个优势并不实用。这主要是因为,在新增数据时,通常会伴随一个查找的动作。例如,在第五个结点后,新增一个新的数据结点,那么执行的操作就包含两个步骤:

第一步,查找第五个结点;

第二步,再新增一个数据结点。整体的复杂度就是 O(n) + O(1)。

线性表真正的价值在于,它对数据的存储方式是按照顺序的存储。如果数据的元素个数不确定,且需要经常进行数据的新增和删除时,那么链表会比较合适。如果数据元素大小确定,删除插入的操作并不多,那么数组可能更适合些。

3.线性表案例

例 1 链表的翻转。给定一个链表,输出翻转后的链表。例如,输入1 ->2 -> 3 -> 4 ->5,输出 5 -> 4 -> 3 -> 2 -> 1。

对于某个单向链表,它的指针结构造成了它的数据通路有去无回,一旦修改了某个指针,后面的数据就会造成失联的状态。为了解决这个问题,我们需要构造三个指针 prev、curr 和 next,对当前结点、以及它之前和之后的结点进行缓存,再完成翻转动作。

在这里插入图片描述
注:图片转正网络,侵删

while(curr){
  /*
  从前往后遍历链表,定义三个指针分别指向相邻的三个结点,反转前两个结点,即让第二个结点指向第一个结点。然后依次往后移动指针,直到第二个结点为空结束,再处理链表头尾即可。
  */
  next = curr.next; // 存储curr的下一个节点地址
  curr.next = prev; // 把prev地址赋值给curr的下一个节点,即指针方向反转
  prev = curr; // 把curr地址赋值给prev,即prev位置后移
  curr = next; // 把next(之前curr的next)赋值给curr,即curr后移
}

例 2 给定一个奇数个元素的链表,查找出这个链表中间位置的结点的数值。

这个问题也是利用了链表的长度无法直接获取的不足做文章,解决办法如下:

  • 一个暴力的办法是,先通过一次遍历去计算链表的长度,这样我们就知道了链表中间位置是第几个。接着再通过一次遍历去查找这个位置的数值。

  • 除此之外,还有一个巧妙的办法,就是利用快慢指针进行处理。其中快指针每次循环向后跳转两次,而慢指针每次向后跳转一次。如下图所示。

在这里插入图片描述
注:图片转正网络,侵删

while(fast && fast.next && fast.next.next){
    fast = fast.next.next;
    slow = slow.next;
}

例 3 判断链表是否有环。如下图所示,这就是一个有环的链表。

在这里插入图片描述
假设链表有环,快指针每次走两格,而慢指针每次走一格,相对而言,快指针每次循环会多走一步。这就意味着:

  • 如果链表存在环,快指针和慢指针一定会在环内相遇,即 fast == slow 的情况一定会发生。

  • 反之,则最终会完成循环,二者从未相遇。

如下图所示:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值