这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战
一、了解题目
附上原题链接:234. 回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
二、题解分析
1、降低空间复杂度的思考
在做题时,我们通常要尽可能的去降低时间复杂度和空间复杂度。那对于降低空间复杂度来说,主要有以下思考:
- 我们要尽量地去避免使用
O(n)的额外空间,通常使用的方法就是改变输入; - 因此,我们可以将链表的后半部分进行反转(修改链表结构),然后将前半部分和后半部分进行比较;
- 比较完成后我们应该将链表恢复原样;
- 虽然不需要恢复也能通过测试用例,但是使用该函数的人通常不希望链表结构被更改;
- 该方法虽然可以将空间复杂度降到
O(1),但是在并发环境下,该方法也有缺点; - 在并发环境下,函数运行时需要锁定其他线程或进程对链表的访问,因为在函数执行过程中链表会被修改。
2、题解步骤
依据以上题意和对复杂度的分析。整个流程可以分为以下五个步骤:
- 找到前半部分链表的尾节点;
- 反转后半部分链表;
- 判断是否回文;
- 恢复链表;
- 返回结果。
三、代码实现
依据上面的题解,我们将用 js 来实现这道题。具体实现代码如下:
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
// 翻转链表
const reverseList = (head) => {
let prev = null;
let curr = head;
while (curr !== null) {
let nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
// 找到前半部分的尾节点
const endOfFirstHalf = (head) => {
let fast = head;
let slow = head;
while (fast.next !== null && fast.next.next !== null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
var isPalindrome = function(head) {
// 1.首先先判断链表是否为空,为空时则直接返回true
if (head == null) return true;
// 2.找到前半部分链表的尾节点并反转后半部分链表
const firstHalfEnd = endOfFirstHalf(head);
const secondHalfStart = reverseList(firstHalfEnd.next);
// 3.判断是否是回文
let p1 = head;
let p2 = secondHalfStart;
let result = true;
while (result && p2 != null) {
if (p1.val != p2.val) result = false;
p1 = p1.next;
p2 = p2.next;
}
// 4.还原链表并返回结果
firstHalfEnd.next = reverseList(secondHalfStart);
return result;
};
四、复杂度分析
时间复杂度: O(n) ,其中 n 指的是链表的大小。
空间复杂度: O(1) 。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1) 。
思路来源:回文链表
以上就是关于回文链表的题解,不知道对小伙伴们是否有帮助呢?
我们下期见👋👋👋