数据结构与算法第三讲 - 链表

本讲内容

链表定义和分类
链表和数组比较
链表操作
写链表代码的技巧
简单算法题

链表定义和分类

定义:通过指针把零散的内存空间串联起来存储数据
思想:空间换时间

对于执行较慢的程序,可以通过消耗更多的内存(空间换时间)来进行优化;而消耗过多内存的程序,可以通过消耗更多的时间(时间换空间)来降低内存的消耗。

分类:单链表,循环链表,双向链表,双向循环链表

单链表

循环链表是一种特殊的单链表,单链表尾指针指向null,循环链表尾指针指向头节点

循环链表
双向链表

单链表是单向的,每个节点只有一个指针,而双向链表每个节点有两个指针,所以占用的空间更大,但是它的操作相比于单链表更灵活高效。
接下来我们看下主要高效在什么地方?
删除操作:

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

第一种情况,不管是单链表还是双向链表,为了查找到值等于给定值的结点,都需要从头结点开始一个一个依次遍历对比,直到找到值等于给定值的结点,然后再通过我前面讲的指针操作将其删除。尽管单纯的删除操作时间复杂度是 O(1),但遍历查找的时间是主要的耗时点,对应的时间复杂度为 O(n)。根据时间复杂度分析中的加法法则,删除值等于给定值的结点对应的链表操作的总时间复杂度为 O(n)。
第二种情况,我们已经找到了要删除的结点,但是删除某个结点 q 需要知道其前驱结点,而单链表并不支持直接获取前驱结点,所以,为了找到前驱结点,我们还是要从头结点开始遍历链表,直到 p->next=q,说明 p 是 q 的前驱结点。
对于单链表,需要从头遍历找到该节点的前向节点指针,然后指向删除操作,删除操作时间复杂度O(1),但是遍历节点的时间复杂度为 O(n),所以根据复杂度计算的加法原则,总时间复杂度为 O(n)
对于双向链表,找到的节点包含前向节点的指针,因此不需要再进行遍历,找到前向节点O(1),删除节点O(1),所以总时间复杂度为 O(1)

双向循环链表

链表和数组比较

分类 内存 优点 缺点
数组 连续内存,可以随机读取 结构简单,使用容易,可以利用CPU的缓存 1. 大小固定,为避免越界一般会超额申请,即使有容器类的数组,但是在扩容时,会先拷贝现有的数据,性能比较差
链表 零散内存,需要通过指针进行连接和取值 插入删除操作时间复杂度O(1),频繁的插入删除会导致内存碎片多,需要进行垃圾回收 占用空间多
image.png

链表操作

  1. 插入和删除


    单链表的插入和删除
  2. 查找

写链表代码技巧

技巧一:理解指针或引用的含义

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

技巧二:警惕指针丢失和内存泄漏

示例:
单链表的插入


单链表的插入

p->next = x; // 将p的next指针指向x结点;
x->next = p->next; // 将x的结点的next指针指向b结点;

以上代码会导致指针丢失,b节点后的节点无法访问

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

正确写法:

x->next = p->next; // 将x的结点的next指针指向b结点;
p->next = x; // 将p的next指针指向x结点;

删除链表结点时,也一定要记得手动释放内存空间,否则,也会出现内存泄漏的问题。当然,对于像 Java 这种虚拟机自动管理内存的编程语言来说,就不需要考虑这么多了。

注:什么是内存泄漏?
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory(内存溢出)!

技巧三:利用哨兵简化实现难度

  • 为什么要引入哨兵?
    针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。
    单链表插入
    其他节点插入

x->next = p->next; // 将x的结点的next指针指向b结点;
p->next = x; // 将p的next指针指向x结点;

第一个节点的插入

if (head == null) { head = new_node;}

单链表删除
其他节点删除

p->next = p->next->next;

最后一个节点删除

if (head->next == null) { head = null;}

如果我们引入哨兵结点,在任何时候,不管链表是不是空,head 指针都会一直指向这个哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表

带头链表

哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现逻辑了。

技巧四:重点留意边界条件处理

要实现没有 Bug 的链表代码,一定要在编写的过程中以及编写完成之后,检查边界条件是否考虑全面,以及代码在边界条件下是否能正确运行。

常需检查的边界情况:

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

技巧五:举例画图,辅助思考

技巧六:多写多练,没有捷径

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

简单算法题

如何实现LRU缓存淘汰算法?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容

  • 部分摘自专栏评论 1.关于缓存和缓存淘汰策略 什么是缓存?缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中...
    ssas_阅读 127评论 0 0
  • 概念 链表的插入,只需要上一个节点的指针指向这个新增的节点,新增的节点指向下一个节点。删除类似操作。 链表当前节点...
    回忆只能等候阅读 464评论 0 0
  • 一、什么是链表? 和数组一样,链表也是一种线性表。 从内存结构来看,链表的内存结构是不连续的内存空间,是将一组零散...
    蹩脚的小三阅读 1,031评论 0 0
  • 数组和链表 数据是使用连续的内存空间存储数据。 链表是使用不连续的内存空间存储数据。 常见链表链表结构 单链表 循...
    Jackie_Zheng阅读 344评论 0 0
  • 前几节学习了「链表」、「时间与空间复杂度」的概念,本节将结合「循环链表」、「双向链表」与 「用空间换时间的设计思想...
    五分钟学算法阅读 1,705评论 0 14