原题链接: https://leetcode.com/problems/palindrome-linked-list/
1. 题目介绍
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?
可以使用O(1)的空间复杂度吗?
2. 解题思路
可以将链表从中间一分为二,然后将后半部分反转。之后再比较两个链表是否一样即可。如果一样,则是回文串。
涉及2个知识点:
- 将链表从中间一分为二。
- 将链表反转
2.1 将链表从中间一分为二
如果想要在O(1)空间复杂度下实现,可以采用双指针法。
设定两个指针,一快一慢,两者一开始都在头节点。快指针每次走2步,慢指针每次走1步。当快指针走到结尾时,慢指针会刚好走到第 n/2 个节点。假如 n 为奇数,慢指针所指的节点就是链表的中点。
2.2 反转一个链表
如果想要在O(1)空间复杂度下实现反转链表,可以让每一个节点的next指针都指向前一个节点,这样遍历一遍链表就可以实现反转整个链表了,无需新建节点。
实现代码
class Solution {
public boolean isPalindrome(ListNode head) {
if( head == null || head.next == null){
return true;
}
//将链表从中间一分为二,并且统计链表的长度
ListNode fast = head;
ListNode slow = head;
ListNode slowPre = null;
int length = 0;
while(fast != null && fast.next != null){
fast = fast.next.next;
slowPre = slow;
slow = slow.next;
length += 2;
}
slowPre.next = null;
if(fast != null){
length += 1;
}
//将后半部分链表反转
//如果长度为奇数,则将链表中点之后的部分反转,不包括中点
ListNode before = null;
ListNode next = null;
ListNode l = (length % 2 == 0 ? slow : slow.next);
while(l != null){
next = l.next;
l.next = before;
before = l;
l = next;
}
//反转后的链表的头结点是before所指向的节点
//比较前半部分链表和经过反转的后半部分链表是否相同
ListNode l1 = head;
ListNode l2 = before;
while(l1 != null ){
if(l1.val == l2.val){
l1 = l1.next;
l2 = l2.next;
}else{
return false;
}
}
return true;
}
}