leetcode234.迴文鏈表

請判斷一個鏈表是否爲迴文鏈表。

示例 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;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章