题目来源:Leetcode234,是一道easy题。
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我们知道单向链表只有指向后面的指针,所以不能像数组那样,从两端向中间遍历,不过。
很自然的,我们可以利用栈的性质来存储链表,然后弹出和原链表比较,时空复杂度为N/N
/**
* 用栈性质实现
* @param head
* @return
*/
public boolean isPalindrome(ListNode head){
Stack<ListNode> stack=new Stack<>();
ListNode curr=head;
while (curr!=null){
stack.push(curr);
curr=curr.next;
}
while (head!=null){
if(head.val!=stack.pop().val){
return false;
}
head=head.next;
}
return true;
}
然而,面试官一般不会就这样放过我们,让我们在时空复杂度上进行优化。从时间复杂度上来说,已经做到了2*N(入栈出栈),这题目也不可能用二分法优化的log(n),所以,我们可以从空间复杂度上入手。
回文字符的定义是什么?围绕着中点对称,由此,我们可以先找到链表的中点,把链表分为两部分,这两部分是逆序排列的,于是,我们可以将前一段链表反转,然后和后一段链表进行比较!
涉及到的知识
1、链表反转问题,在我之前发的文章中有非常详细的解释,这里就不多说了。
2、寻找链表中点问题,思路是快慢指针,在前面文章也有解读
/**
* 解题思路:在找中点的时候对前半部分进行翻转,时空复杂度为n和1
* @param head
* @return
*/
public boolean isPalindrome(ListNode head){
if(head==null||head.next==null){
return true;
}
ListNode quick=head;
ListNode slow=head;
ListNode prev=null;
ListNode curr=head;
while (quick!=null&&quick.next!=null){
ListNode temp=slow.next;
quick=quick.next.next;
slow=slow.next;
curr.next=prev;
prev=curr;
curr=temp;
}
if(quick!=null){
//奇数个元素,跳过中间节点
slow=slow.next;
}
ListNode frontNode=prev;
while (frontNode!=null){
if(frontNode.val!=slow.val){
return false;
}
frontNode=frontNode.next;
slow=slow.next;
}
return true;
}
值得注意的是,在寻找链表中点的过程中,如果链表长度为偶数,慢指针最终指向中点靠后的那个节点,快指针为null,如果链表长度为奇数,那么慢指针最终指向正中间的节点,因此,在比较之前需要指向自己的后一个节点。