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

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

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

    image.png
    可以借用一下集合,集合可以通过索引从两头向中间一一遍历元素,遍历的同时就可以一一比较元素值是否相同:

    1. public boolean isPalindrome(ListNode head) {
    2. List<Integer> valList = new ArrayList<>();
    3. // 遍历链表,将节点放入 List
    4. ListNode cur = head;
    5. while (cur != null) {
    6. valList.add(cur.val);
    7. cur = cur.next;
    8. }
    9. // 双指针从 List 两头同时遍历并比较是否相同
    10. int left = 0;
    11. int right = valList.size() - 1;
    12. while (true) {
    13. // 走到同一个节点,表示链表的节点数量是奇数
    14. if (left == right) {
    15. return true;
    16. }
    17. // 左指针走到了右指针后侧,表示链表的节点数量是偶数
    18. if (left > right) {
    19. return true;
    20. }
    21. // 只要有某个值不同,则表示不是回文链表
    22. if (!valList.get(left).equals(valList.get(right))) {
    23. return false;
    24. }
    25. left++;
    26. right--;
    27. }
    28. }

    通过集合的思路,如果直接在链表中能够也找到两个指针,那么是否也可以达到相同的效果呢?

    假设链表的节点数量是偶数:
    image.png
    假设链表的节点数量是奇数:
    image.png

    而 876. 链表的中间结点 情况和上述类似:
    1)链表的节点数量是奇数时,和上述节点数量是奇数的情况一致
    2)链表的节点数量是偶数时,和上述节点数量是偶数的情况稍稍有点不同,上述情况找的是链表前半部分的最后一个节点,而在链表的中间结点中是链表后半部分的第一个节点

    1. /**
    2. * 找到链表中点
    3. * 节点数量是奇数时,则刚好是中间节点
    4. * 节点数量是偶数时,则刚好是后半部分第一个节点
    5. * @param head
    6. * @return
    7. */
    8. public ListNode middleNode(ListNode head) {
    9. // 快慢指针都指向头结点
    10. ListNode fast = head;
    11. ListNode slow = head;
    12. // 快指针或者快指针的下一个节点是 null 时
    13. while(fast != null && fast.next != null) {
    14. // 慢指针走一步,快指针走两步
    15. slow = slow.next;
    16. fast = fast.next.next;
    17. }
    18. // 慢指针指向中点
    19. return slow;
    20. }

    稍稍改动一下上述算法:

    1. /**
    2. * 找到链表中点
    3. * 节点数量是奇数时,则刚好是中间节点
    4. * 节点数量是偶数时,则刚好是前半部分最后一个节点
    5. * @param head
    6. * @return
    7. */
    8. public ListNode middleNode(ListNode head) {
    9. // 快慢指针都指向头结点
    10. ListNode fast = head;
    11. ListNode slow = head;
    12. // 快指针的下一个节点或者下下一个节点是 null 时
    13. while (fast.next != null && fast.next.next != null) {
    14. // 慢指针走一步,快指针走两步
    15. slow = slow.next;
    16. fast = fast.next.next;
    17. }
    18. return slow;
    19. }

    那么直接利用双指针算法如下:

    1. public boolean isPalindrome(ListNode head) {
    2. if (head == null) {
    3. return true;
    4. }
    5. // 找到前半部分链表的尾节点并反转后半部分链表
    6. ListNode firstHalfEnd = endOfFirstHalf(head);
    7. ListNode secondHalfStart = reverseList(firstHalfEnd.next);
    8. // 判断是否回文
    9. ListNode slow = head;
    10. ListNode fast = secondHalfStart;
    11. boolean isPalindrome = true;
    12. while (isPalindrome && fast != null) {
    13. if (slow.val != fast.val) {
    14. isPalindrome = false;
    15. }
    16. slow = slow.next;
    17. fast = fast.next;
    18. }
    19. // 还原链表并返回结果
    20. firstHalfEnd.next = reverseList(secondHalfStart);
    21. return isPalindrome;
    22. }
    23. /**
    24. * 找到链表前半部分最后一个节点
    25. *
    26. * @param head
    27. * @return
    28. */
    29. public ListNode endOfFirstHalf(ListNode head) {
    30. ListNode fast = head;
    31. ListNode slow = head;
    32. while (fast.next != null && fast.next.next != null) {
    33. fast = fast.next.next;
    34. slow = slow.next;
    35. }
    36. return slow;
    37. }
    38. /**
    39. * 反转链表
    40. *
    41. * @param head
    42. * @return
    43. */
    44. public ListNode reverseList(ListNode head) {
    45. ListNode prev = null;
    46. ListNode curr = head;
    47. while (curr != null) {
    48. ListNode nextTemp = curr.next;
    49. curr.next = prev;
    50. prev = curr;
    51. curr = nextTemp;
    52. }
    53. return prev;
    54. }