請判斷一個鏈表是否爲迴文鏈表。
示例 1:
輸入: 1->2
輸出: false
示例 2:
輸入: 1->2->2->1
輸出: true
進階:
- 你能否用 O(n) 時間複雜度和 O(1) 空間複雜度解決此題?
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/palindrome-linked-list
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
完整代碼
題目分析:
本道題的重點在於如何保證代碼的空間複雜度爲O(N),這就表明瞭解題過程中不能用棧等數據結構來暫存鏈表的節點。
剛開始看這道題目想到了逆轉鏈表,但感覺有點麻煩,就沒有繼續嘗試,後來看了評論區的討論也是用到了鏈表逆轉,果斷嘗試了這種方法。
基本思想:
- 找到鏈表的中間節點
- 將中間節點之後的鏈表逆轉
- 從頭開始和逆轉後的鏈表的每個節點一 一比較
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(head == NULL || head->next == NULL)
return true;
//1.找到鏈表最中間的節點
ListNode * slow = head, * fast = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}//循環結束後,slow指向最中間的節點(鏈表中有奇數個節點),或者回文的開始節點(鏈表中有偶數個節點)
//2.將slow後面的鏈表逆轉
ListNode * cur = slow, *pre = NULL;
while(cur){
ListNode * next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}//循環結束後,pre指向逆轉後的鏈表的頭節點
//3.判斷是否是迴文
while(head != slow){
if(head->val != pre->val)
return false;
head = head->next;
pre = pre->next;
}
return true;
}
};
參考:官方題解
解法一:
定義一個和鏈表長度相同的數組,定義兩個指針,頭指針,尾指針,比較頭尾指針指向的元素是否相等。
時間複雜度O(N),空間複雜度O(N)
解法二:
遞歸
依然是將第一個元素和最後一個元素比較,藉助遞歸可以實現鏈表的從後往前的比較,遞歸過程中將節點先放入棧中,當走到最後一個節點時,再進行比較。
時間複雜度O(N),空間複雜度O(n)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode* firstNode = head;
return recursive(head, firstNode);
}
private:
bool recursive(ListNode* head, ListNode* &firstNode){
if(head != NULL){
if(!recursive(head->next, firstNode))//目的是遞歸到最後一個節點,遞歸過程中,若是有不滿足條件的直接返回false
return false;
if(head->val != firstNode->val)//比較兩個節點的值是否相等
return false;
firstNode = firstNode->next;//在之前的節點都是迴文 且當前節點相等的情況下,繼續比較下一個
}
return true;
}
};
上述第一種方法的優化
基本思想:
逆轉前半部分鏈表,邊尋找中間節點,邊進行逆轉。
還有一點需要說明,在判斷鏈表是否是迴文的時候將鏈表進行逆轉了,這是使用該函數時不希望有的情況,可以在判斷結束後,將改變後的鏈表再逆轉回來。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(head == NULL || head->next == NULL)
return true;
//1.尋找中間節點,同時逆轉前半部分
ListNode * slow = head, * fast = head, * pre = NULL, * cur;
while(fast && fast->next){
cur = slow;
slow = slow->next;
fast = fast->next->next;
cur->next = pre;
pre = cur;
}
//循環結束 ,pre指向逆轉後的頭節點(原鏈表前半部分的尾),slow指向後半部分節點
//2.判斷是否迴文
ListNode * l_head = pre;
ListNode * r_head = slow;
if(fast)//鏈表中節點是奇數的情況
r_head = r_head->next;
while(l_head && r_head && l_head->val == r_head->val){
l_head = l_head->next;
r_head = r_head->next;
}
bool res = (l_head == NULL ? true : false);
//3.將逆轉後的鏈表還原
ListNode * temp = pre, * next;
pre = NULL;
while(temp){
next = temp->next;
temp->next = pre;
pre = temp;
temp = next;
}
pre->next = slow;
return res;
}
};