【leetcode】第143題 重排鏈表 3種方法求解 C++

題目:重排鏈表

給定一個單鏈表 L:L0→L1→…→Ln-1→Ln ,
將其重新排列後變爲: L0→Ln→L1→Ln-1→L2→Ln-2→…

你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。

示例 1:

給定鏈表 1->2->3->4, 重新排列爲 1->4->2->3.
示例 2:

給定鏈表 1->2->3->4->5, 重新排列爲 1->5->2->4->3.

在這裏插入圖片描述

方法1:遍歷一遍鏈表,把結點存入棧中,然後再遍歷一次鏈表,將棧中的結點依次插到鏈表前面;
當遍歷到的結點或它的下一結點和棧中的結點相同時,結束插入,同時斷開剩下的結點的連接
時間複雜度 O(n) 空間複雜度 O(n)

	void reorderList(ListNode* head) {
        if(head == nullptr || head->next == nullptr || head->next->next == nullptr) return;
        stack<ListNode* > nodes;
        ListNode* p = head;
        while(p){                       // 先將結點存入棧中
            nodes.push(p);
            p = p->next;
        }
        p = head;
        while(p && !nodes.empty()){     // 將棧中的結點插到鏈表前面
            ListNode* tmp = nodes.top();// 取棧頂結點
            nodes.pop();
            if(p == tmp){               // 鏈表長度爲奇數的情況,當前結點與棧頂結點相同則結束
                p->next = nullptr;
                break;
            }else if(p->next == tmp){   // 鏈表長度爲偶數的情況,當前結點的下一節點與棧頂結點相同則結束(不需要再插入了)
                p->next->next = nullptr;
                break;
            }else{   					// 插入鏈表
                tmp->next = p->next;
                p->next = tmp;
                p = tmp->next;
            }
        }
    }

方法2:先找到鏈表中點,從中點斷開鏈表,然後翻轉中點之後的鏈表,最後遍歷倆鏈表合併爲一個鏈表
時間複雜度 O(n) 空間複雜度 O(1)

	void reorderList(ListNode* head) {
        if(head == nullptr || head->next == nullptr || head->next->next == nullptr) return;

        ListNode* mid_node = findMidOfList(head);  // 找中點
        ListNode* right_head = mid_node->next;
        mid_node->next = nullptr;                  // 斷開中點之後的鏈表

        right_head = reverseList(right_head);      // 翻轉右半部分

        ListNode* left_p  = head;                  // 用於遍歷左半部鏈表
        ListNode* right_p = right_head;            // 用於遍歷右半部鏈表
        while(left_p && right_p){                  // 合併倆鏈表
            ListNode* temp_next1 = left_p->next;   // 暫存下一結點
            ListNode* temp_next2 = right_p->next;  // 暫存下一結點
            left_p->next  = right_p;               // 右部分鏈表的結點插入左部分結點之後
            right_p->next = temp_next1;
            left_p  = temp_next1;                  // 指針後移
            right_p = temp_next2;
        }

    }
    // 查找鏈表的中間結點並返回
    ListNode* findMidOfList(ListNode* head){
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast && fast->next){
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
    // 翻轉鏈表,返回翻轉後鏈表的頭結點
    ListNode* reverseList(ListNode* head){
        ListNode* pre_node = nullptr;
        ListNode* cur_node = head;
        while(cur_node){
            ListNode* next_node = cur_node->next;
            cur_node->next = pre_node;
            pre_node = cur_node;
            cur_node = next_node;
        }
        return pre_node;
    }

方法3:遞歸法,遞歸地斷開最後一個結點,直到鏈表只剩一個或兩個節點,然後層層返回開始拼接
時間複雜度較高 約爲 O(n^2) 空間複雜度約爲 O(n), 因爲每次遞歸減少兩個節點(首尾結點),遞歸深度近似爲 n。

    void reorderList(ListNode* head){
        if(head == nullptr || head->next == nullptr) return;
        ListNode* pre = nullptr;
        ListNode* p   = head;
        while(p->next){   // p 遍歷到最後一個結點,pre 遍歷到倒數第二個結點
            pre = p;
            p = p->next;
        }
        pre->next = nullptr;              // 斷開最後一個結點
        ListNode* temp_next = head->next; // 暫存頭結點的下一節點,便於拼接
        reorderList(temp_next);

        head->next = p;
        p->next    = temp_next;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章