Leetcode 234. 回文鏈表(進階)
阿新 • • 發佈:2018-11-01
mar strong 參考 isp 主體 簡單 p s color 題目
題目描述:
請判斷一個鏈表是否為回文鏈表。
示例 1:
輸入: 1->2 輸出: false
示例 2:
輸入: 1->2->2->1 輸出: true
進階:
你能否用 O(n) 時間復雜度和 O(1) 空間復雜度解決此題?
解法一:(空間復雜度O(n))
遍歷一遍鏈表壓棧,借助棧把鏈表倒序,然後依次比較“原鏈表元素”和“新棧中元素”,如果都相等則返回true,否則返回false。
這樣簡單粗暴,代碼的主體包含在解法二中了,這裏不列出了。
另外,這種解法的時間要求能不能通過Leetcode的測試,我沒有試過,因為覺得沒必要試。
解法二:(空間復雜度O(n/2))
解題思路:使用兩個指針,fast和slow指針。
(1)fast指針每次走兩步,slow指針每次走一步;
(2)fast指針走到鏈表末尾的時候,slow指針走到鏈表的中間位置結點(鏈表長度n為偶數)或中間位置的前一個結點(鏈表長度n為奇數);
(1)——>(2)——>(3)——>(2)——>(1) slow fast (1)——>(2)——>(3)——>(3)——>(2) ——> (1) slow (fast) 多走1步fast
(3)slow直接到了中間,就可以將整個鏈表的後半部分壓棧實現逆序,依次和前半部分比較,思路同解法一。
註:就是在這裏,額外的時間復雜度減少了n/2,因為只需要將鏈表中一半的元素壓棧。
其他的細節,代碼裏有詳細註釋。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ static const auto __ = []() {// turn off sync std::ios::sync_with_stdio(false); // untie in/out streams std::cin.tie(nullptr); return nullptr; }(); class Solution { public: bool isPalindrome(ListNode* head) { //額外空間復雜度O(n/2) /*使用兩個指針,fast指針每次走兩步,slow指針每次走一步;fast指針走到鏈表末尾的時候, slow指針走到鏈表的中間位置結點(鏈表長度n為偶數)或中間位置的前一個結點(鏈表長度n為奇數) */ //判空 if(head == NULL) return true; //單節點鏈表 if(head->next == NULL) return true; ListNode* fast = head; ListNode* slow = head;//指向第一個結點 //fast指針指向末尾結點,slow指針指向中間位置結點或中間位置的前一個結點 ////註意:這裏的結束判斷主要看fast!!! while(fast->next != NULL && fast->next->next != NULL ) { fast = fast->next->next; slow = slow->next; } //鏈表長度為偶數,fast指針最後多走一步到鏈表末尾 if(fast->next) fast = fast->next; stack<int> s; //將鏈表後半部分元素壓棧,通過棧來實現逆序 while(slow->next) { s.push(slow->next->val); slow = slow->next; } //依次比較前半部分元素和逆序的後半部分元素 while(!s.empty()) { if(s.top() != head->val) return false; //前、後一起往後移動 s.pop(); head = head->next; } return true; } };
解法三:(進階:空間復雜度O(1))
解題思路:解法三和解法二的區別在於,最後不使用棧來倒序鏈表後半部分的元素,而是選擇直接本地操作(額外空間復雜度為O(1)),在原鏈表上將後半部分元素倒置(反轉),比較完後得出結果後,再 還原鏈表,返回結果。
代碼中有詳細註釋。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ static const auto __ = []() { // turn off sync std::ios::sync_with_stdio(false); // untie in/out streams std::cin.tie(nullptr); return nullptr; }(); class Solution { public: bool isPalindrome(ListNode* head) { //額外空間復雜度O(1) //判空 if(head == NULL) return true; //單節點鏈表 if(head->next == NULL) return true; //雙節點鏈表 if(head->next->next == NULL) if(head->val == head->next->val) return true; else return false; ListNode* fast = head; ListNode* slow = head;//指向第一個結點 //fast指針指向末尾結點,slow指針指向中間位置結點或中間位置的前一個結點 ////註意:這裏的結束判斷主要看fast!!! while(fast->next != NULL && fast->next->next != NULL ) { fast = fast->next->next; slow = slow->next; } //鏈表長度為偶數,fast指針最後多走一步到鏈表末尾 if(fast->next) fast = fast->next; //-----區別在這裏,元素不壓棧,直接將鏈表後半部分元素逆序,比較完後得出結果後,再還原鏈表,返回結果--------// //---------------鏈表的後半部分元素“倒序”-------------------// ListNode* p = slow->next; ListNode* q = NULL; ListNode* cur = NULL; slow->next = NULL; while(p) { cur = p->next; p->next = q; q = p; p = cur; } //依次比較 前半部分元素 和 逆序的後半部分元素 while(1) { if(fast->val != head->val) { //鏈表復原 ListNode* m = q->next; ListNode* n = NULL; ListNode* cur2 = NULL; q->next = NULL; while(m){ cur2 = m->next; m->next = n; n = m; m = cur2; } slow->next = n; return false; } //前、後一起往後移動 fast = fast->next; head = head->next;
//--------在這裏判斷結束,是調試的結果----//
//針對這種情況:(3)——>(1)——>(2)——>(3),後半部分反轉之後的鏈表是(3)——>(1)——>(3)——>(2)
// 如果使用while(head->next)作為結束,會少比較一次,也就是最後(1)和(2)不會比較到,從而出錯 if(fast == NULL) break; } //鏈表復原 ListNode* m = q->next; ListNode* n = NULL; ListNode* cur2 = NULL; q->next = NULL; while(m){ cur2 = m->next; m->next = n; n = m; m = cur2; } slow->next = n; return true; } };
參考資料:
https://blog.csdn.net/blioo/article/details/62050967 單向鏈表反轉(倒置)問題
Leetcode 234. 回文鏈表(進階)