Given a singly linked list, determine if it is a palindrome.
Example 1:
Input: 1->2 Output: false
Example 2:
Input: 1->2->2->1 Output: true
Follow up:
Could you do it in O(n) time and O(1) space?
这一题,主要价值就在于follow up上。如果没有O(1) space的限制的话做法太多,譬如自下而上的递归,或者更加直观可以用stack。这两者的本质是一样的,只是自下而上的递归写起来还是有点意思,可以认为是自下而上的一种基础练习。具体就不解释了,非常直白。直接给代码吧。
public boolean isPalindrome(ListNode head) {
if (head == null) return true;
return recurCheck(new ListNode[]{head}, head);
}
public boolean recurCheck(ListNode[] head, ListNode cur) {
if (cur.next == null) {
boolean res = head[0].val == cur.val;
head[0] = head[0].next;
return res;
} else {
boolean res = recurCheck(head, cur.next);
if (res) {
res &= head[0].val == cur.val;
head[0] = head[0].next;
}
return res;
}
}
当然还有一种做法也很简单,reverse 这个list并且在reverse的过程里创造一个新的list,然后比较两个list是不是一样,具体就不写了。提供一下思路罢了
关于follow up,其实我觉得这可以归档到medium了。思维很定式也很取巧。利用到了两题的做法,Reverse Linked List和Linked List Cycle里快慢指针的思想。作为Palindrome的东西,可以认为是头尾往中间扫是一样的,但换一种想法,就是一个palindrome的东西,你反转它的后半部分(如果节点数为奇数可以忽略中间那个节点),那么它们前后两半是一样的。
这个follow up的解决思路就在此,
1. 利用快慢指针找到后半部分的起点。譬如1 -> 2 -> 2 -> 1里,如果你有快慢指针两个,一个一次走一步,一个一次走两步,那么当快指针走到终点的时候,慢指针刚好能走到第三个节点2那里。
2. 然后反转后半部分list,使之成为1->2->1->2。后半部分可以和前半部分分离,没所谓。
3. 你反转的过程里可以得到反转后的后半部分的head节点。也就是你这个时候有一个指针指向第一个节点1,另一个指针指向第三个节点1。
4. 同时前进,直到第二个指针走到尽头,如果一路匹配,则ok,否则吃屎。
根据以上思路,给出代码如下:
public boolean isPalindrome(ListNode head) {
if (head == null) return true;
ListNode slow = head, fast = head, prev = null;
while (fast != null) {
prev = slow;
slow = slow.next;
fast = fast.next == null ? fast.next : fast.next.next;
}
prev.next = reverseList(slow);
slow = head;
fast = prev.next;
while (fast != null) {
if (slow.val != fast.val) return false;
slow = slow.next;
fast = fast.next;
}
return true;
}
public ListNode reverseList(ListNode head) {
ListNode prev = null, cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}