題目描述
請判斷一個鏈表是否為回文鏈表。
示例 1:
輸入: 1->2
輸出: false
示例 2:
輸入: 1->2->2->1
輸出: true
進階:
你能否用 O(n) 時間復雜度和 O(1) 空間復雜度解決此題?
思路
數組加雙指針
首先給出一個比較簡單明了的思路,雙指針。還記得用雙指針的方法來判斷回文字符串,但是單鏈表無法反向進行索引,需要先將單鏈表轉換成數組再進行判斷。
bool isPalindrome(ListNode* head) {
vector<int> listVal;
while(head)
{
listVal.push_back(head->val);
head = head->next;
}
int iPre = 0;
int iEnd = listVal.size() - 1;
while(iPre < iEnd)
{
if(listVal[iPre] != listVal[iEnd])
return false;
iPre++;
iEnd--;
}
return true;
}
反轉單鏈表解法
找到單鏈表的中間結點。
- 鏈表長度為奇數(2n+1)時索引為n。
如1->2->3->2->1, 則中間結點為3. - 鏈表長度為偶數2n時為n-1。
如1->2->3->3->2->1, 則中間結點為第一個3.
將中間結點之后的一半鏈表進行反轉。
反轉之后只需要進行一次掃描就可以得出結論。
如1->2->3->2->1經過反轉之后變為1->2->3->1->2。
1->2->3->3->2->1經過反轉之后變為1->2->3->3->2->1。
接下來要解決的兩個問題就是如何找到鏈表的中間的結點和如何反轉鏈表。
鏈表中間結點的確定可以采取快慢指針的方法,快指針每次循環前進兩步,慢指針每次循環前進一步,當快指針到達鏈表的結尾時,慢指針指向的結點恰好是鏈表的中間結點。
ListNode *fast = head;
ListNode *slow = head;
while(fast->next != NULL && fast->next->next!= NULL)
{
fast = fast->next->next;
slow = slow->next;
}
反轉單鏈表仍然可以采用雙指針的方法。
ListNode* reverseList(ListNode *head)
{
ListNode *pre = NULL;
ListNode *next = NULL;
while(head != NULL)
{
next = head->next;
head->next = pre;
pre = head;
head = next;
}
return pre;
}
以鏈表1->2->3->4->5為例簡單分析下上述代碼的反轉過程。
1 2->3->4->5
1<-2 3->4->5
1<-2<-3 4->5
1<-2<-3<-4 5
1<-2<-3<-4<-5
最終解法。
bool isPalindromePro(ListNode* head)
{
if(head == NULL || head->next == NULL)
return true;
ListNode *fast = head;
ListNode *slow = head;
while(fast->next != NULL && fast->next->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
}
slow->next = reverseList(slow->next);
slow = slow->next;
while(slow != NULL)
{
if(head->val != slow->val)
return false;
head = head->next;
slow = slow->next;
}
return true;
}
ListNode* reverseList(ListNode *head)
{
ListNode *pre = NULL;
ListNode *next = NULL;
while(head != NULL)
{
next = head->next;
head->next = pre;
pre = head;
head = next;
}
return pre;
}