首先是题目:
这道题被定义为简单题着实让我感到很意外,如果没有时间和空间复杂度限制的情况下确实是简单题,毕竟如果没有空间限制直接可以先遍历链表,然后将链表节点值用数组存起来,然后用个双指针去判断这个数组是否是回文数组即可。
如果没有空间限制的话,我还想到了可以先将链表复制一份,然后将复制后的链表进行反转操作,然后遍历原始和反转后的链表,如果完全相等的话则说明这条链表是回文链表,如果有一个节点不相等则说明不是反转链表。
我的代码如下:
class Solution {
public ListNode reverseListNode(ListNode head){
ListNode pre = null;
ListNode cur = head;
while(cur!=null){
ListNode node = cur.next;
cur.next=pre;
pre=cur;
cur = node;
}
return pre;
}
public boolean isPalindrome(ListNode head) {
if(head==null) return true;
ListNode n_node = head;
ListNode new_head = new ListNode(head.val);
ListNode n_new=new_head;
while(n_node.next!=null){
n_new.next = new ListNode(n_node.next.val);
n_node = n_node.next;
n_new = n_new.next;
}
ListNode node = reverseListNode(new_head);
while(head!=null){
if(head.val==node.val){
head = head.next;
node = node.next;
continue;
}
else{
return false;
}
}
return true;
}
}
然后看了一下题解:题解的前两种方法也是没有顾及空间复杂度,方法和我想的差不多。下面说说它的第三种解法吧。
方法三:
避免使用 O(n) 额外空间的方法就是改变输入。
我们可以将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较。比较完成后我们应该将链表恢复原样。虽然不需要恢复也能通过测试用例,因为使用该函数的人不希望链表结构被更改。
算法:
我们可以分为以下几个步骤:
1.找到前半部分链表的尾节点。
2.反转后半部分链表。
3.判断是否为回文。
4.恢复链表。
5.返回结果。
执行步骤一,我们可以计算链表节点的数量,然后遍历链表找到前半部分的尾节点。
或者可以使用快慢指针在一次遍历中找到:慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针到链表的中间。通过慢指针将链表分为两部分。
若链表有奇数个节点,则中间的节点应该看作是前半部分。
步骤二可以使用在反向链表问题中找到解决方法来反转链表的后半部分。
步骤三比较两个部分的值,当后半部分到达末尾则比较完成,可以忽略计数情况中的中间节点。
步骤四与步骤二使用的函数相同,再反转一次恢复链表本身。
说白了它的解法就是,结合了快慢指针和链表反转的方法,首先利用快慢指针找到链表的中间节点,然后再以中间节点为头节点将链表的一半进行反转,反转完了以后判断前一半和反转后的后一半是否相等。判断链表是否是回文链表以后再将反转的一半链表再进行反转从而恢复成原始链表。这种思路可以说是非常的巧妙了。
代码如下:
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null) return true;
// Find the end of first half and reverse second half.
ListNode firstHalfEnd = endOfFirstHalf(head);
ListNode secondHalfStart = reverseList(firstHalfEnd.next);
// Check whether or not there is a palindrome.
ListNode p1 = head;
ListNode p2 = secondHalfStart;
boolean result = true;
while (result && p2 != null) {
if (p1.val != p2.val) result = false;
p1 = p1.next;
p2 = p2.next;
}
// Restore the list and return the result.
firstHalfEnd.next = reverseList(secondHalfStart);
return result;
}
// Taken from https://leetcode.com/problems/reverse-linked-list/solution/
private ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
private ListNode endOfFirstHalf(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}