1. 写在前面
今天分享的题目是leetcode234题。即给你一个链表,判断它是不是回文的。虽然它是一个简单题目,但是里面还有很多值得学习的要点。
2. 使用栈
回文的含义是正着反着念都是一样的,如果我们可以的得到链表逆序遍历的结果,那么就比较容易判断了。想到”逆序“,我们考虑到了栈“后进先出”的特性,于是我们可以使用栈来实现链表的逆序遍历。我们首先遍历链表,把结果都加入一个栈中,然后再次遍历链表,每次遍历到一个元素的时候,就从栈中弹出一个元素和它比对,如果不一样那么说明不是回文的。因为从栈中弹出的顺序刚好是链表逆序遍历的结果,如果都能比对成功,那么说明链表是回文的。比较容易理解,我就不画图了,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.util.Stack;
public class PalindromeLinkedListUsingStack { public static boolean isPalindrome(ListNode head) { Stack<Integer> stack = new Stack<>(); ListNode cur = head; for(; cur != null; cur = cur.next) stack.push(cur.val); for(cur = head; cur != null; cur = cur.next, stack.pop()) { if(cur.val != stack.peek()) return false; } return true; } static class ListNode { int val; ListNode next; ListNode(int v) { this.val = v; } } }
|
时间复杂度$O(N)$,空间复杂度$O(N)$。如果是笔试,做到这里就可以结束了,但是如果在面试中,面试官一定会问你,有没有更加省空间的办法?下面就来整活了。
3. 反转链表
我们如果可以把链表的右半部分逆序,同时从左边和右边开始遍历,如果链表是回文的,那么一定可以一一对应。
如果想要把链表的右半部分反转,首先我们需要找到链表的中点,通过观察可以看出,如果是奇数个元素,就要找到中点的位置,偶数个则需要找到上中点。那么如何找到链表的中点呢?请记住下面的算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class FindMedian { public static ListNode find(ListNode head) { if(head = null) return null; ListNode slow = head, fast = head; while(fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; } return slow; } static class ListNode { int val; ListNode next; ListNode(int v) { this.val = v; } } }
|
这个方法大家可以下去验证两个例子,记住即可。
如果找到了中点元素$median$,那么我们需要把$median.next$反转,拿到反转后的链表逐一比对即可。注意如果发现链表不是回文的,一定要暂时记住结果,把链表再反转回来后返回,一般来说是不可以修改输入的数据的。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class PalindromeLinkedListUsingReverse { public static boolean isPalindrome(ListNode head) { if(head.next == null) return true; ListNode slow = head, fast = head; while(fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; } ListNode head1 = head, head2 = reverse(slow.next), join = head2; boolean ans = true; while(head2 != null && ans) { if(head1.val != head2.val) ans = false; head1 = head1.next; head2 = head2.next; } reverse(join); return ans; } ListNode reverse(ListNode head) { ListNode pre = null, cur = head, next; while(cur != null) { next = cur.next; cur.next = pre; pre = cur; cur = next; } return pre; } static class ListNode { int val; ListNode next; ListNode(int v) { this.val = v; } } }
|
时间复杂度$O(N)$,空间复杂度$O(1)$。