《剑指 Offer》学习记录:题 24:反转链表

题 24:反转链表#

题干#

定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。——《剑指 Offer》P142

测试样例#

链表的数据结构定义如下(Python):

Copy Highlighter-hljs
class ListNode: def __init__(self, x): self.val = x self.next = None

若传入的头结点 head 指向的链表结构如图所示:

则返回的头结点 head 指向的链表结构为:

迭代法#

方法思路#

想要翻转链表,其实就是修改每个结点的指针域,使其指向该结点的前驱。修改时,对于每个结点而言有 2 个点需要格外注意,其一是保存每个结点的前驱,第二是记录结点的后继,防止丢失信息而无法访问后续的结点。对于整张链表而言,需要注意链表翻转之后,原本的首元结点的后继应该为 NULL,而原本的尾结点将成为新的首元结点。

方法模拟#

例如有如下的链表,使用一个指针 ptr 指向首元结点,pre 指向该结点的前驱(首元结点没有前驱,因此指向 NULL),nex 指向 ptr->next。使用 3 个指针记录是为了在修改结点的前驱、后继关系时,能够做到不丢信息。

在记录 3 个结点的信息之后,可以把 ptr 的后继修改为其前驱 pre。

修改该结点的后继关系后,需要把 ptr 和 pre 往后移动,这样才能继续处理剩余的结点。因为 ptr 指针的信息还在,可以直接把 pre 移动到 ptr 的位置上。

接着把 ptr 移动到 nex 的结点上:

令 nex = ptr->next,就可以更新 nex 指针。经过对原来的首元结点进行调整,现在的链表状态如下,重复上述操作即可实现翻转链表。

解题代码#

Copy Highlighter-hljs
class Solution: def reverseList(self, head: ListNode) -> ListNode: ptr = head #ptr 指向首元结点 pre = None #pre 初始化为 NULL while ptr: nex = ptr.next #修改 ptr 的后继 ptr.next = pre #将 ptr 和 pre 向后移动 pre = ptr ptr = nex return pre

时空复杂度#

设链表的结点数为 n,算法的流程需要遍历整张链表,因此时间复杂度为 O(n)。
由于只需要 3 个指针存储信息,而不需要其他辅助空间,空间复杂度为 O(1)。

递归法#

从题解——【反转链表】:双指针,递归,妖魔化的双指针中学习到的。

方法思路#

递归法是一种巧妙的方法,充分利用了递归回溯的功能。由于链表不支持随机访问,因此想要访问第 i 个结点,就需要先访问第 i-1 个结点。如果将思路逆转过来,翻转链表可以视为将原来的尾结点当做头结点,做尾插法。此时的关键点有 2 个,其一是如何将原来的尾结点当做首元结点返回,其二是如何反向取到每一个结点。

方法模拟#

这时可以充分利用递归,首先递归遍历整个链表,递归函数每次传入结点 ptr 的后继 ptr-> next。

当遍历到尾结点时开始回溯,此时递归函数的返回值是尾结点,因为这样就类似于接力棒,可以把尾结点作为翻转后的链表的头结点传回来。至于如何实现“尾插法”,注意此时递归的层次已经回到了尾结点的前驱,因此可以通过 ptr->next 取到当前递归层次所在的结点的后继,这样就可以进行修改。

再次递归回溯,就可以继续取到前面的结点了,继续 ptr->next 取到当前结点的后继进行操作。不断重复上述操作,即可完成链表逆序。

题解代码#

Copy Highlighter-hljs
class Solution: def reverseList(self, head: ListNode) -> ListNode: #遍历完所有结点作为递归出口 if(head == None or head.next == None): return head #返回尾结点 tail = self.reverseList(head.next) #接收返回的尾结点 head.next.next = head #修改 head->next->next 的指向 head.next = None return tail #仍然返回尾结点,向前传递

时空复杂度#

设链表的结点数为 n,算法的流程需要遍历整张链表,因此时间复杂度为 O(n)。
由于递归的深度和结点数 n 相同,保留递归的状态需要额外的空间,空间复杂度为 O(1)。

参考资料#

《剑指 Offer(第2版)》,何海涛 著,电子工业出版社
【反转链表】:双指针,递归,妖魔化的双指针

posted @   乌漆WhiteMoon  阅读(109)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
CONTENTS