【数据结构】链表 的介绍与python实现 上篇

本文部分文字图片引用了极客时间的《数据结构与算法之美》链表篇 https://time.geekbang.org/column/article/41013
讲解的很不错的课程,如果有需要可以去订阅。

链表介绍

链表通过指针将一组零散的内存块串联在一起。
内存块称为链表的“结点”。
为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结点的地址。
如图所示,我们把这个记录下个结点地址的指针叫作后继指针 next
单链表
习惯性地把第一个结点叫作头结点,把最后一个结点叫作尾结点
尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。

数组与链表

数组需要一块连续的内存空间来存储。
而链表恰恰相反,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用。
数组与链表

分类

单链表、双向链表和循环链表
分类

插入和删除操作

链表的插入和删除操作,我们只需要考虑相邻结点的指针改变,所以对应的时间复杂度是 O(1)。
链表随机访问的性能没有数组好,需要 O(n) 的时间复杂度。
插入删除

关于删除额外要说的

尽管单纯的删除操作时间复杂度是 O(1),但遍历查找的时间是主要的耗时点,对应的时间复杂度为 O(n)。

单链表并不支持直接获取前驱结点,所以,为了找到前驱结点,我们还是要从头结点开始遍历链表。

删除一个数据无外乎这两种情况:

  1. 删除结点中“值等于某个给定值”的结点;
  2. 删除给定指针指向的结点。

第一种情况,不管是单链表还是双向链表,都需要从头结点开始一个一个依次遍历对比,时间复杂度为 O(n)。

第二种情况,单链表还是要遍历来获取前驱节点,双向链表可以支持 O(1) 时间复杂度的情况下找到前驱结点。
单链表删除操作需要 O(n) 的时间复杂度,而双向链表只需要在 O(1) 的时间复杂度内就搞定了。

使用技巧

理解指针或引用的含义

将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。
p->next=q。这行代码是说,p 结点中的 next 指针存储了 q 结点的内存地址。

警惕指针丢失和内存泄漏

插入结点时,一定要注意操作的顺序。

删除链表结点时,也一定要记得手动释放内存空间。

利用哨兵简化实现难度

针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。

// 插入
new_node->next = p->next;
p->next = new_node;

// 插入(头部)
if (head == null) {
  head = new_node;
}

// 删除
p->next = p->next->next;

// 删除(头部)
if (head->next == null) {
   head = null;
}

重点留意边界条件处理

  • 如果链表为空时,代码是否能正常工作?
  • 如果链表只包含一个结点时,代码是否能正常工作?
  • 如果链表只包含两个结点时,代码是否能正常工作?
  • 代码逻辑在处理头结点和尾结点的时候,是否能正常工作?

举例画图,辅助思考

链表操作图

练习

如果想做链表的相关练习,以便更熟悉链表结构,可以尝试下面的代码实现操作。

  • 单链表反转
  • 链表中环的检测
  • 两个有序的链表合并
  • 删除链表倒数第 n 个结点
  • 求链表的中间结点

这些问题的代码可以参考:https://github.com/wangzheng0822/algo/blob/master/python/07_linkedlist/linked_list_algo.py
另外我还对部分问题进行了代码的解读,请参考:
链表中环的检测,求单链表的中间结点
删除链表倒数第n个结点

Python代码实现链表,请看下篇:【链表】链表的介绍与python实现 下篇

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值