《剑指 Offer》学习记录:题 24:反转链表
题 24:反转链表#
题干#
定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。——《剑指 Offer》P142
测试样例#
链表的数据结构定义如下(Python):
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 指针。经过对原来的首元结点进行调整,现在的链表状态如下,重复上述操作即可实现翻转链表。
解题代码#
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 取到当前结点的后继进行操作。不断重复上述操作,即可完成链表逆序。
题解代码#
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版)》,何海涛 著,电子工业出版社
【反转链表】:双指针,递归,妖魔化的双指针
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)