https://leetcode.com/problems/palindrome-linked-list/
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?
解题思路:
CC150中的原题2.7。
有好几种解法。首先,如果题目不要求O(1)的空间的话。使用快慢指针,找到list的中点的同时,将前半段的每个结点的值加入到一个stack中。后面从中点向后遍历的同时,逐个弹出栈顶元素。用这种方式完成前半段的倒置。
需要注意的是,如何判断list的结点数量是奇数个的方法。如果最后fast!=null的话,说明是奇数个。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ public class Solution { public boolean isPalindrome(ListNode head) { Stack<Integer> stack = new Stack<Integer>(); ListNode fast = head, slow = head; 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; } }
注意点,先push再next,否则[1,2]的case1不会被push进去,就是tru了。第二,判断条件不是while (fast.next =! null && fast.next.next != null) 。
但是,本题要求是O(1)的空间。其实也很简单,但是代码复杂了一些。获得list的中点后,将右半段转置。然后两个指针,分别指向左半段的head和右半段转置后的head,往后逐个遍历对比。
最后再将右半段转置回去。因为不能破坏原list的结构。事实上,如果不转置也能AC。
需要注意的是,reverse(ListNode head)的方法,是转置以head开头的链表,但是并不改变head的前一个结点的next一直指向head。所以,无论右半段如何转置,前一个结点都不会变,也不会引起死循环。这是写给自己看的话。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ public class Solution { public boolean isPalindrome(ListNode head) { ListNode fast = head, slow = head; while(fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; } // 奇数个结点 if(fast != null) { slow = slow.next; } // 右半侧反向 ListNode right = reverse(slow); ListNode left = head; boolean res = true; while(right != null) { if(left.val != right.val) { res = false; break; } left = left.next; right = right.next; } // 恢复右半侧 reverse(right); return res; } public ListNode reverse(ListNode head) { ListNode pre = null; while(head != null) { ListNode next = head.next; head.next = pre; pre = head; head = next; } return pre; } }