回文链表

LeetCode每日一题,234. Palindrome Linked List

先看题目描述

Bkqwbd.png

大意就是给定一个链表,让我们判断它是否为回文链表

算法和思路

递归

这个思路参照了前几天做的题重排链表里的递归的算法

思路就是当给定一个链表后,若我们已知链表的头节点和尾节点,并且可以判断头节点和尾节点之间的部分是否为回文链表,那么这个问题就很好解决。于是就希望实现一个递归函数,可以判断中间部分是否是回文链表,并且可以返回尾节点。因此就想到构造这样一个递归函数:

  • 若头节点和尾节点之间的部分是回文链表,则返回尾节点
  • 若中间的部分不是回文链表,则返回 null

那么我们实现该函数以后,只需再对头节点和尾节点的值进行比较,然后返回 true 或 false 即可

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

这个算法的效率没有上一个递归算法的运行效率高

整个流程可以分为以下五个步骤:

  • 通过快慢指针找到前半部分链表的尾节点
  • 反转后半部分链表
  • 判断是否回文
  • 恢复链表
  • 返回结果

执行步骤一,我们可以计算链表节点的数量,然后遍历链表找到前半部分的尾节点

我们也可以使用快慢指针在一次遍历中找到:慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分

若链表有奇数个节点,则中间的节点应该看作是前半部分

步骤二可以使用 206. 反转链表 问题中的解决方法来反转链表的后半部分

步骤三比较两个部分的值,当后半部分到达末尾则比较完成,可以忽略计数情况中的中间节点

步骤四与步骤二使用的函数相同,再反转一次恢复链表本身

算法源码

递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
int len = 0;
ListNode node = head;
while (node != null) {
node = node.next;
len++;
}
if (len == 1) return true;
if (len == 2) {
return head.val == head.next.val;
}
ListNode tail = getPalindromeTail(head.next, len - 2);
return tail != null && head.val == tail.val;
}

private ListNode getPalindromeTail(ListNode head, int n) {
if (n == 1) return head.next;
if (n == 2) {
if (head.val == head.next.val) {
return head.next.next;
} else {
return null;
}
}
ListNode tail = getPalindromeTail(head.next, n - 2);
if (tail == null || head.val != tail.val) {
return null;
}
return tail.next;
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null) {
return true;
}

// 找到前半部分链表的尾节点并反转后半部分链表
ListNode firstHalfEnd = endOfFirstHalf(head);
ListNode secondHalfStart = reverseList(firstHalfEnd.next);

// 判断是否回文
ListNode p1 = head;
ListNode p2 = secondHalfStart;
boolean result = true;
while (result && p2 != null) {
if (p1.val != p2.val) {
result = false;
}
p1 = p1.next;
p2 = p2.next;
}

// 还原链表并返回结果
firstHalfEnd.next = reverseList(secondHalfStart);
return result;
}

private ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}

private ListNode endOfFirstHalf(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}