问题
如何判断一个链表是不是回文?要求解法的时间复杂度是O(n),并且不得使用超过O(1)的辅助空间。如果一个链表是回文,那么链表的节点序列从前往后看和从后往前看是相同的。
分析
如果不考虑辅助空间的限制,直观的解法是创建一个新的链表,链表中节点的顺序和输入链表的节点顺序正好相反。如果新的链表和输入链表是相同的,那么输入链表就是一个回文链表。只是这种解法需要创建一个和输入链表长度相等的链表,因此需要O(n)的辅助空间。
仔细分析回文链表的特点以便找出更好的解法。回文链表的一个特性是对称性,也就是说,如果把链表分为前后两半,那么前半段链表反转之后与后半段链表是相同的。在如图4.13所示的包含6个节点的链表中,前半段链表的3个节点反转之后分别是3、2、1,后半段链表的3个节点也分别是3、2、1,因此它是一个回文链表。
链表的节点总数是偶数。如果链表的节点总数是奇数,那么把链表分成前后两半时不用包括中间节点。例如,一个链表中的节点顺序是1、2、k、2、1,前面两个节点反转之后是2、1,后面两个节点也是2、1,不管中间节点的值是什么该链表都是回文链表。
解
public class Test {
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode11 = new ListNode(1);
ListNode listNode22 = new ListNode(2);
ListNode listNode33 = new ListNode(3);
ListNode listNode4 = new ListNode(4);
ListNode listNode5 = new ListNode(5);
ListNode listNode6 = new ListNode(6);
ListNode listNode7 = new ListNode(7);
ListNode listNode8 = new ListNode(8);
ListNode listNode9 = new ListNode(9);
listNode1.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode33;
listNode33.next = listNode22;
listNode22.next = listNode11;
boolean result = isPalindrome(listNode1);
System.out.println(result);
}
public static boolean isPalindrome(ListNode head) {
if (head == null || head.next == null) {
return true;
}
ListNode slow = head;
ListNode fast = head.next;
// slow和fast按照两个两个的遍历下去,如果最后fast.next==null,则说明总个数是奇数
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 static 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;
}
public static 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;
}
}