题目

题目来源:力扣(LeetCode)

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:
image.png
输入:head = [1,2,2,1]
输出:true

示例 2:
image.png
输入:head = [1,2]
输出:false

思路分析

双指针法

首先,遍历链表将节点值存储到数组列表中。我们使用 currentNode 指向当前节点,每次迭代时向数组添加 currentNode.val,并更新 currentNode = currentNode.next ,当 currentNode.next 为 null 时链表循环结束。

然后定义两个指针,prev 指针指向数组列表的起点位置,last 指针指向数组列表的结尾位置,遍历数组,每次迭代时判断两个指向指向的元素是否相同,若不同,则返回 false;若相同,则将两个指针同时向内移动,并继续判断,直到两个指针相遇。

  1. /**
  2. * Definition for singly-linked list.
  3. * function ListNode(val, next) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.next = (next===undefined ? null : next)
  6. * }
  7. */
  8. /**
  9. * @param {ListNode} head
  10. * @return {boolean}
  11. */
  12. var isPalindrome = function (head) {
  13. const vals = [];
  14. // 遍历链表,将链表的节点值存储到数组列表中
  15. while (head !== null) {
  16. vals.push(head.val);
  17. head = head.next;
  18. }
  19. // 定义两个指针,分别指向数组列表的起点位置和结尾位置
  20. // 每一次迭代判断两个指针指向的元素是否相同
  21. // 若不同,返回 false;相同则将两个指针向内移动,并继续判断,直到两个指针相遇
  22. for (let prev = 0, last = vals.length - 1; prev < last; ++prev, --last) {
  23. if (vals[prev] !== vals[last]) {
  24. return false;
  25. }
  26. }
  27. return true;
  28. };


递归

使用递归反向迭代节点,同时使用递归函数外的变量向前迭代,就可以判断链表是否为回文。

首先递归链表节点,直到指针 currentNode 指向尾节点,由于递归的特性,会再从后往前进行比较。frontPointer 是递归函数外的指针。若 currentNode.val != frontPointer.val 则返回 false。反之,frontPointer 向前移动并返回 true。

  1. let frontPointer;
  2. const recursivelyCheck = (currentNode) => {
  3. if (currentNode !== null) {
  4. if (!recursivelyCheck(currentNode.next)) {
  5. return false;
  6. }
  7. if (currentNode.val !== frontPointer.val) {
  8. return false;
  9. }
  10. frontPointer = frontPointer.next;
  11. }
  12. return true;
  13. }
  14. var isPalindrome = function(head) {
  15. frontPointer = head;
  16. return recursivelyCheck(head);
  17. };

快慢指针 + 反转后半部分链表

我们可以通过快慢指针找到链表的中间节点,然后以中间节点将链表分为前后两个部分,将后半部分链表反转,然后比较前半部分链表和后半部分链表,比较完成后将链表恢复原样。

具体步骤:

  1. 通过快慢指针找到链表的中间节点
  2. 以中间节点将链表分割为前后两部分链表
  3. 反转后半部分链表
  4. 比较前后两部分链表,判断是否回文
  5. 恢复链表
  6. 返回结果

在步骤1 中,使用快慢指针在一次遍历中找到链表的中间,快指针每次走两步,慢指针每次走一步,快慢指针同时出发,当快指针走到链表的末尾时,慢指针恰好走到链表的中间。我们以慢指针所指向的节点作为分割点,将链表分割为前后两个部分。

步骤2 中以链表中间节点分割链表时,由于是以中间节点作为分割点,若链表有奇数个节点,那么中间的节点应该开作是前半部分链表的节点。

分割完链表后,我们需要将后部分链表反转。

在步骤4 中,按照后半部分链表的长度,比较前后两部分链表的节点值是否相等,当后半部分链表到达末尾时,比较完成,可以忽略前半部分链表的尾节点,即原链表的中间节点。

比较完成后,需要将后半部分链表进行恢复,我们通常不希望链表结构被更改。

234. 回文链表 - 图3

代码:

  1. /**
  2. * Definition for singly-linked list.
  3. * function ListNode(val, next) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.next = (next===undefined ? null : next)
  6. * }
  7. */
  8. /**
  9. * @param {ListNode} head
  10. * @return {boolean}
  11. */
  12. // 反转链表
  13. const reverseList = (head) => {
  14. let prev = null;
  15. let curr = head;
  16. while (curr !== null) {
  17. let nextTemp = curr.next;
  18. curr.next = prev;
  19. prev = curr;
  20. curr = nextTemp;
  21. }
  22. return prev;
  23. }
  24. // 找到前半部分链表的尾节点
  25. const endOfFirstHalf = (head) => {
  26. let fast = head; // 慢指针
  27. let slow = head; // 快指针
  28. // 快指针走两步,慢指针走一步,当快指针走到链表的末尾时,慢指针恰好到达链表的中间,然后通过慢指针将链表分为两部分
  29. while (fast.next !== null && fast.next.next !== null) {
  30. fast = fast.next.next;
  31. slow = slow.next;
  32. }
  33. return slow;
  34. }
  35. var isPalindrome = function(head) {
  36. if (head == null) return true;
  37. // 找到前半部分链表的尾节点
  38. const firstHalfEnd = endOfFirstHalf(head);
  39. // 反转后半部分链表
  40. const secondHalfStart = reverseList(firstHalfEnd.next);
  41. // 判断是否回文
  42. // 前半部分链表头节点
  43. let p1 = head;
  44. // 后半部分链表头节点
  45. let p2 = secondHalfStart;
  46. // result 默认为 true
  47. let result = true;
  48. // 原链表总长度如果是奇数,那么前半部分链表比后半部分链表多一个节点
  49. // 按照后半部分链表的长度,比较前半部分链表和后半部分链表的节点值是否相等
  50. while (result && p2 != null) {
  51. // 前半部分的链表的节点值与后半部分链表的节点值不相等,说明不是回文链表,返回 false
  52. if (p1.val != p2.val) result = false;
  53. p1 = p1.next;
  54. p2 = p2.next;
  55. }
  56. // 还原链表并返回结果
  57. firstHalfEnd.next = reverseList(secondHalfStart);
  58. return result;
  59. };

参考阅读 https://leetcode-cn.com/problems/palindrome-linked-list/solution/hui-wen-lian-biao-by-leetcode-solution/ https://leetcode-cn.com/problems/palindrome-linked-list/solution/dai-ma-sui-xiang-lu-234-hui-wen-lian-bia-qs0k/