1. 题目描述
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?
检查一个单向链表是否是回文。是否能满足O(n)的时间复杂度,和O(1)的空间复杂度。
2. 解题思路
程序员面试金典p128页原题。上面给出的解法分别是,1.全部翻转之后比较,2. 使用栈存一半的链表,3.使用递归,其中方法12都需要额外的存储空间,方法3虽然不需要但是看起来比较复杂。
我想到的方法就是,首先扫描一遍链表,将后半部分元素翻转,之后开始从两头开始像中间比较。主要是实现起来需要注意的细节比较多,比如奇数个会指到最中间的一个而偶数个会指到中间的后面的那个元素。这就需要找一个具体一些的例子推导一下。
写文章的时候突然想到一个更快的方案,不需要扫描一遍半链表,只需要在慢指针走的时候将前半部分翻转,之后从中间开始往两边比较,只需要1遍的时间即可,但是实验证明好像也没多打败多少人T T。目前没想到更快的方案,想到的可以留言~
3. Code
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
// Code1: 遍历一遍半
public class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) return true;
ListNode p = head, q = p; // 快慢指针
// 找到中点
while(q.next != null)
{
p = p.next;
q = q.next;
if(q.next != null)
{
q = q.next;
}
}
ListNode mid = p, r = null;
q = p.next;
mid.next = null;
// 翻转后半部分
while(q != null)
{
r = q.next;
q.next = p;
p = q;
q = r;
}
q = head; // 当前q指向链表头,p指向链表尾
while(q != mid)
{
if(p.val != q.val) return false;
p = p.next;
q = q.next;
}
if(p != null && p.val != q.val) return false;
return true;
}
}
// Code2:遍历一遍
public class Solution {
public boolean isPalindrome(ListNode head) {
// 一个元素都没有或只有一个元素
if(head == null || head.next == null) return true;
ListNode p = head, q = p.next,r = q,f = p; // 快慢指针
// 找到中点
boolean isEven = true; // 是否为偶数
do
{
// 快指针先走
f = f.next;
if(f.next != null) // 如果是奇数个||没到结尾
{
f = f.next;
isEven = false;
if(q != null){ // 如果q不为空
// 翻转指针,断开后使用r指向后一个
r = q.next;
q.next = p;
p = q;
q = r;
}
}else{ // 如果是偶数个
isEven = true;
}
}while(f.next != null);
if(!isEven) // 如果是奇数个,舍去中间元素
{
p = p.next;
}
// 进行值得比较
while(r != null)
{
if(p.val != r.val) return false;
p = p.next;
r = r.next;
}
return true;
}
}