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;
}
}