Given a singly linked list, determine if it is a palindrome.
Follow up:
Could you do it in O(n) time and O(1) space?
思路:
题目要求判断一个链表是否是回文链表。并且规定时间复杂度为o(n),空间复杂度为o(1)。既然要判断一个链表是否回文,并且空间复杂度为o(1),所以并不能使用辅助的容器来存储节点中的元素,只能把链表一分为二。所以首先得找到链表的中间节点,并且因为链表的特性,如果链表一分为二之后都从各自的头节点开始往后搜素是没有办法判断回文特性的。必须将第二个链表进行翻转。
1.找到中间节点
使用快慢指针法。它的意思是维护两个指针fast和slow,一开始都指向链表的头节点。随后每次fast向后移动两个节点,slow移动一个节点,知道fast==null时停止。这样,如果链表长度为2n,slow会指向第n+1个节点。如果链表长度为2n+1,slow会指向第n+1个节点。
2.对第二个链表翻转
翻转的操作就是上一题,这里可以直接拿来使用。对于链表长度为2n的情况,slow在第n+1个节点,翻转之后slow指向第2n个节点,第n+1的节点的后继为Null.对于长度2n+1的链表,中间元素不需要比较,所以应该从slow.next节点(即第n+2个节点)开始翻转,翻转之后slow指向第2n+1个节点,第n+2的节点后继为null。
3.此时我们就有了两条链表,一条以slow作为头节点,一条以head作为头节点。只要slow不为空依次比较两条链表的各节点值。
public class PalindromeLinkedList {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null)
return true;
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
if(fast == null) { //链表长度为偶数
slow = ReverseLinkedList.reverseList(slow); //注:这里使用的reverseList未在博客中贴出
} else { //链表长度为奇数
slow = ReverseLinkedList.reverseList(slow.next);
}
while(slow != null) {
if(slow.val != head.val) {
return false;
}
slow = slow.next;
head = head.next;
}
return true;
}
}
如果没有空间复杂度的要求,则可以使用stack的LIFO的特性来辅助实现。同样需要找到中间节点,但是不需要进行翻转链表的操作。
public class PalindromeLinkedList {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null)
return true;
ListNode fast = head;
ListNode slow = head;
Stack<Integer> stack = new Stack<Integer>();
while(fast != null && fast.next != null) {
stack.push(slow.val);
fast = fast.next.next;
slow = slow.next;
}
if(fast != null) {
slow = slow.next;
}
while(slow != null) {
if(slow.val != stack.pop()) {
return false;
}
slow = slow.next;
}
return true;
}
}