根据回文对称的特点,不难想到将链表分成前后两部分,然后将其中一部分反转的方法。
可以使用快慢指针的方式找到链表的中点,其中快指针每次移动两步,慢指针每次移动一步。当快指针到达链表末尾时,慢指针指向的位置即为链表的中点。
然后将链表的后半部分反转,比较前半部分和反转后的后半部分,从而判断链表是否为回文链表。
public boolean isPalindrome(ListNode head) {
// 初始化快慢指针,均指向链表头部
ListNode fast = head;
ListNode slow = head;
// 使用快慢指针找到链表中点
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
// 反转后半部分链表
ListNode root = null;
while (slow != null) {
ListNode tmp = slow.next;
slow.next = root;
root = slow;
slow = tmp;
}
// 比较前半部分和反转后的后半部分链表
fast = head;
while (root != null) {
// 如果节点值不相等,说明链表不是回文链表,返回 false
if (fast.val != root.val) {
return false;
}
// 移动指针
fast = fast.next;
root = root.next;
}
// 遍历完成,说明链表是回文链表,返回 true
return true;
}
这段代码中在处理奇数长度的链表时选择保留中间节点,当然你也可以选择舍弃比较中间节点。如下:
public boolean isPalindrome(ListNode head){
// 如果链表为空或只有一个节点,认为是回文链表
if (head == null || head.next == null){
return true;
}
// 使用快慢指针找到链表中点
ListNode slow = head;
ListNode fast = head.next;
// 当链表长度是奇数时,循环结束时slow位于中间节点的前一个节点
while (fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
}
// 获取后半部分链表的头节点
ListNode secondHalf = slow.next;
if (fast.next != null){
// 当链表长度是奇数时,就要多移动一步
secondHalf = slow.next.next;
}
// 切断前半部分链表与后半部分链表的连接
slow.next = null;
// 比较前半部分和反转后的后半部分链表是否相等
return equals(secondHalf, reverseList(head));
}
// 比较两个链表是否相等
private boolean equals(ListNode head1, ListNode head2){
while (head1 != null && head2 != null){
if (head1.val != head2.val){
return false;
}
head1 = head1.next;
head2 = head2.next;
}
return head1 == null && head2 == null;
}
// 反转链表
private ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}