「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战」
剑指 Offer II 027. 回文链表
一、题意
二、解法
1. 方法一:字符串拼接比较
思路:
通过正向、反向将链表节点值拼接成字符串,最后比较正向、反向字符串是否相同。
- 定义两个临时变量,存储正、反两个拼接的字符串
- 遍历链表,进行字符串拼接,正向的从前往后拼接,反向的从后往前拼接。
- 比较正、反字符串是否相同
代码:
function isPalindrome(head) {
let positiveStr = '';
let reverseStr = '';
while (head) {
const nodeVal = head.val;
// 正向字符串拼接
positiveStr += nodeVal;
// 反向字符串拼接
reverseStr = nodeVal + reverseStr;
// 下一个节点
head = head.next;
}
return positiveStr === reverseStr;
}
复杂度分析
时间复杂度: O(n)
遍历一遍链表,时间复杂度为 O(n) 。
空间复杂度: O(1)
常数个临时变量存储,因此,空间复杂度为 O(1) 。
1. 方法二:链表反转 + 快慢指针
思路:
找到链表的中间节点,将前半部分的链表反转,与后半部分链表数据进行比较。 为了找到中间位 置,采用两个引用,步调速度相差 1,当快的引用到达最终节点时,慢的正好在中间。
- 分别定义快、慢指针,及前半部分的指针存储
- 遍历链表,快指针走 2 步,慢指针走 1 步,快指针走到链表尾部的时候,慢指针位于链表中间。
- 将慢指针对应的前半部分链表进行反转
- 前半部分从head开始遍历,后半部分从慢指针开始遍历,遍历的同时进行比较。
代码:
function isPalindrome(head) {
// 空或者单节点
if (!head || !head.next) {
return true;
}
let slowRef = head; // 慢指针
let fastRef = head; // 快指针
let reverseRef; // 反转前半部分
let reversePreRef; // 反转前一个节点
// 连续 2 个节点都存在
while (fastRef && fastRef.next) {
// 快指针前进 2 步
fastRef = fastRef.next.next;
reverseRef = slowRef;
// 慢指针前进 1 步
slowRef = slowRef.next;
// 反转链表
reverseRef.next = reversePreRef;
// 记录上一个节点
reversePreRef = reverseRef;
}
// 奇数场景
if (fastRef) {
// 中间值不用比较,慢指针直接前进一步
slowRef = slowRef.next;
}
while (reverseRef && slowRef) {
// 链表逐个值比较
if (reverseRef.val !== slowRef.val) {
return false;
}
reverseRef = reverseRef.next;
slowRef = slowRef.next;
}
return true;
}
复杂度分析
时间复杂度: O(n)
遍历了包含有 n 个元素的列表一次,时间复杂度为 O(n) 。
空间复杂度: O(1)
常数个临时变量存储,空间复杂度为 O(1) 。