Leetcode 234. 回文鏈表(進階)


1.題目描述

請判斷一個鏈表是否為回文鏈表。

示例 1:

輸入: 1->2
輸出: false

示例 2:

輸入: 1->2->2->1
輸出: true

進階:
你能否用 O(n) 時間復雜度和 O(1) 空間復雜度解決此題?

 

 

2.解法一:(空間復雜度O(n))

遍歷一遍鏈表壓棧,借助棧把鏈表倒序,然后依次比較“原鏈表元素”和“新棧中元素”,如果都相等則返回true,否則返回false。

這樣簡單粗暴,代碼的主體包含在解法二中了,這里不列出了。

另外,這種解法的時間要求能不能通過Leetcode的測試,我沒有試過,因為覺得沒必要試。

 

 

3.解法二:(空間復雜度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;
    }
};

 

 

4.解法三:(進階:空間復雜度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; } };

 

 

 參考資料:

1.https://blog.csdn.net/blioo/article/details/62050967 單向鏈表反轉(倒置)問題

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM