leetcode 好题笔记 —— [234] 回文链表
leetcode 好题笔记 —— [234] 回文链表
题目描述
给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。如果是,返回 true
;否则,返回 false
。
示例1:
1 | 输入:head = [1,2,2,1] |
示例2:
1 | 输入:head = [1,2] |
提示:
- 链表中节点数目在范围
[1, 105]
内 0 <= Node.val <= 9
实现算法
一、快慢指针
所谓快慢指针,是寻找链表中间节点的有效方法。也可用于判断链表回路是否存在环。
快慢指针算法思想:
-
定义快慢指针fast和slow,起始均位于链表头部。规定fast每次后移2步,slow后移1步;
-
若fast遇到null节点,则表示链表无环,结束;
此时slow点所在节点即为链表的中心节点,奇数链表返回中间值,偶数列表返回中间偏前值。
-
若链中有环,fast和slow一定会再次相遇;
-
当fast和slow相遇时,额外创建指针ptr,并指向链表头部,且每次后移1步,最终slow和ptr会在入环点相遇。
实现代码:
1 | def helfListNode_of_list(self, head): |
2. 递归反转链表
这里的递归比较难理解,为了翻转链表,首先使用递归找到最后一个节点,在下文的实现代码中,我将其定义为 head_next ,而遍历到此时的 head 的节点即为 最后节点 head_next 的父节点,即倒数第二个节点
1 | # 子节点指向父节点,并切断父节点指向子节点 |
以此递归回去,使每一个节点都指向父节点,而切断原父节点对子节点的指向。需要注意的是,我们在此过程中并未对最后节点 head_next 进行任何操作,所以最终返回的便是翻转后的链表头结点。
实现代码:
1 | def reserve_list(self, head:ListNode) -> ListNode: |
3. 总接口函数
**目的操作:**找到前半部分链表的尾节点并反转后半部分链表,对两串列表进行一一对应操作以此判断链表是否为回文链表。
实现代码:
1 | class Solution: |
复杂度分析
-
时间复杂度:O(n),其中 n 指的是链表的大小。
-
空间复杂度:O(1)。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)
二、递归
算法思想:
currentNode 指针是先到尾节点,由于递归的特性再从后往前进行比较。frontPointer 是递归函数外的指针。若 currentNode.val != frontPointer.val 则返回 false。反之,frontPointer 向前移动并返回 true。
算法的正确性在于递归处理节点的顺序是相反的(回顾上面打印的算法),而我们在函数外又记录了一个变量,因此从本质上,我们同时在正向和逆向迭代匹配。
1 | class Solution: |
复杂度分析
- 时间复杂度:O(n),其中 n 指的是链表的大小。
- 空间复杂度:**O(n) **,其中 n 指的是链表的大小。