LeetCode 234 难度 easy
参考:公众号 labuladong 如何高效判断回文单链表?
插播:
- 快慢指针法可以更快速地判断一个链表、数组等的长度是奇数还是偶数,都不用进行遍历一遍,不用使用 长度模 2 来判断。
- 直接判断 fast指针是否为空即可
- 为空,则为 偶数
- 不为空 奇数
判断回文链表
①隐式地调用系统的栈来实现双指针
神奇的思路,利用 链表的后序遍历框架
链表不像字符串一样,可以使用下标迭代,所以,无法使用双指针技巧,头尾一起迭代比较。
因此,将链表全部反转,然后进行比较,是一种方法。
但是,可以不用显式
地反转链表。
使用内存中的栈,来进行后序遍历。
神奇!运用了内存中的栈,实现后序遍历,实现从尾部读取的效果!!
对比二叉树的遍历框架:
void traverse(TreeNode root) {
// 前序遍历代码
traverse(root.left);
// 中序遍历代码
traverse(root.right);
// 后序遍历代码
}
由于链表也是递归结构,因此,也一样有遍历框架
void traverse(ListNode head) {
// 前序遍历代码
traverse(head.next);
// 后序遍历代码
}
这个框架有什么指导意义呢?如果我想正序打印链表中的val值,可以在前序遍历位置写代码;反之,如果想倒序遍历链表,就可以在后序遍历位置操作:
/* 倒序打印单链表中的元素值 */
void traverse(ListNode head) {
if (head == null) return;
traverse(head.next);
// 后序遍历代码
print(head.val);
}
因此,可以使用后序遍历框架,模拟头尾指针的操作!!
// 注意,要使用双指针,这里要定义全局变量 left,这样在递归的时候正确使用
let left
var isPalindrome = function(head) {
left = head
return postorderTravel(head.next)
};
function postorderTravel(right){
if(right == null){
return true
}
// 后序遍历框架
let result = postorderTravel(right.next)
result = result && (left.val === right.val)
left = left.next
return result
}
②优化时间复杂度
1.使用快慢指针找链表的中点(也是判断奇偶的好方法)
let slow = head
let fast = head
while(fast != null && fast.next != null){
slow = slow.next
fast = fast.next.next
}
2.如果fast
没有指向null
的话,链表长度为奇数,slow
还需要往前走一步
// 如果是奇数的情况下,fast不为空,slow在中心位置,需要再往前走一个位置
if(fast != null){
slow = slow.next
}
3.从slow
开始反转后面的链表,现在就可以开始比较回文串了:
let left = head
let end = reverseList(slow)
let right = end
let begin = null // 记录slow之前的那个位置
while(right != null){
if(left.val !== right.val){
return false
}
if(right.next == null){
begin = left
}
left = left.next
right = right.next
}
// 复原
begin.next = reverseList(end)
return true
4.复原链表
// 找到slow之前的那个位置,记录下来,为了后面复原
// right再往前走是空的时候,left再往前走就是slow,所以提前记录
// if(left.next === slow) 跑不通,也就是说 JS 中指针的值无法比较
if(right.next == null){
begin = left
}
5. 代码书写
由于没有判断特殊情况,导致报错
if(head == null || head.next == null){
return true
}
一样的,反转链表中没有判断特殊情况 head.next == null
,报错
if(head == null || head.next == null){
return head
}
function reverseList(head){
if(head == null || head.next == null){
return head
}
const last = reverseList(head.next)
head.next.next = head
head.next = null
return last
}
var isPalindrome = function(head) {
if(head == null || head.next == null){
return true
}
let slow = head
let fast = head
while(fast != null && fast.next != null){
slow = slow.next
fast = fast.next.next
}
// 如果是奇数的情况下,fast不为空,slow在中心位置,需要再往前走一个位置
if(fast != null){
slow = slow.next
}
let right = reverseList(slow)
let begin = null
let end = right
let left = head
while(right != null){
if(left.val != right.val){
return false
}
// 找到slow之前的那个位置,记录下来,为了后面复原
// right再往前走是空的时候,left再往前走就是slow,所以提前记录下来
// if(left.next === slow) 跑不通,也就是说 JS 中指针的值无法比较
if(right.next == null){
begin = left
}
left = left.next
right = right.next
}
// 复原
begin.next = reverseList(end)
return true