leetcode148.排序鏈表

在 O(n log n) 時間複雜度和常數級空間複雜度下,對鏈表進行排序。

示例 1:

輸入: 4->2->1->3
輸出: 1->2->3->4

示例 2:

輸入: -1->5->3->4->0
輸出: -1->0->3->4->5

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/sort-list
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

完整代碼

看到時間複雜度爲O(nlogn)的要求,就考慮用快排來實現,但是傳統的快排(以第一個元素爲基準)無法實現鏈表的移動,這裏是單向鏈表。採用瞭如下的移動方法(以第一個元素爲基準)
快排

#include<bits/stdc++.h>

using namespace std;

void swap(int &a, int &b){//交換兩個元素
    int temp = a;
    a = b;
    b = temp;
}
void quickSort(vector<int> &nums, int s, int e){
    if(s >= e)//遞歸終止條件
        return;
    int flag = nums[e];
    int i = s - 1;
    for(int j = s; j < e; ++j){
        if(nums[j] < flag){
            swap(nums[j], nums[++i]);
        }
    }
    swap(nums[++i], nums[e]);
    quickSort(nums, s, i - 1);
    quickSort(nums, i + 1, e);
}

int main(){
    vector<int> nums = {4, 2, 3, 1};
    quickSort(nums, 0, nums.size() - 1);
    for(auto a : nums)
        cout << a << " ";
    cout <<"Dsa";
    return 0;
}

基本思想
快排

  • 交換兩個節點時, 這裏只是交換的鏈表的值,並沒有改變鏈表的指向
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        //快排來實現
        if(head == NULL || head->next == NULL)
            return head;
        
        ListNode* n_head = new ListNode(0), *end_Node;
        n_head->next = head;//指向真正的頭節點的節點
        ListNode* p = head;
        while(p){//尾節點
            end_Node = p;
            p = p->next;
        }
        fastSort(n_head, end_Node);
        return n_head->next;
    }
private:
    void fastSort(ListNode* n_head, ListNode* end_Node){
        if(n_head == end_Node)//遞歸結束的條件
            return;
        ListNode* flag = end_Node;
        ListNode* res = n_head, *left = n_head->next;
        while(left != end_Node){
            if(left->val < end_Node->val){
                res = res->next;
                swap(res, left);//所需交換的兩個節點的前驅                
            }
            left = left->next;
        }
        //res是左半部分末尾的節點
        swap(end_Node, res->next);//已經排好序的值放在了res->next中        
        fastSort(n_head, res);
        fastSort(res->next, end_Node);        
    }
    void swap(ListNode* a, ListNode* b){//交換兩個節點的值
        int temp = a->val;
        a->val = b->val;
        b->val = temp;
    }
};

在排序算法中除了快排,還有歸併排序的時間複雜度也是O(nlogn)
歸併排序
參考:添加鏈接描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        //歸併排序
        if(head == NULL || head->next == NULL)
            return head;
        ListNode *slow = head, *fast = head->next;
        while(fast != NULL && fast->next != NULL){//尋找中間結點
            slow = slow->next;
            fast = fast->next->next;
        }
        ListNode* right_head = slow->next;
        slow->next = NULL;
        ListNode* left = sortList(head);
        ListNode* right = sortList(right_head);
        //歸併鏈表
        ListNode* dummy = new ListNode(0), *p;
        p = dummy;
        while(left && right){
            if(left->val < right->val){
                p->next = left;
                left = left->next;
            }
            else{
                p->next = right;
                right = right->next;
            }
            p = p->next;
        }
        if(left)
            p->next = left;
        if(right)
            p->next = right;
        return dummy->next;
    }
};

非遞歸形式的歸併排序
基本思想:
對於整個鏈表而言,先兩兩合併,再四四合並,知道待合併的長度大於等於鏈表長度時不再合併

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        //歸併:從低向上,非遞歸形式
        if(head == NULL || head->next == NULL)
            return head;
        //1.統計鏈表長度
        int len = 0;
        ListNode *p = head;
        while(p){
            p = p->next;
            ++len;
        }
        int intv = 1;//間隔以2的整數次冪的方式遞增
        ListNode *res = new ListNode(0);
        res->next = head;
        while(intv < len){
            ListNode *pre = res;
            p = res->next;
            while(p){//將間隔爲intv的鏈表合併
                //第一段鏈表
                ListNode* h1 = p;
                int i = intv;
                while(i > 0 && p){
                    p = p->next;
                    i--;
                }
                if(p == NULL)//沒有第二段鏈表,無需合併
                    break;
                //第二段鏈表,(長度不一定是intv)
                ListNode* h2 = p;
                i = intv;
                while(i > 0 && p){
                    p = p->next;
                    i--;
                }
                //合併兩段鏈表
                int l1 = intv, l2 = intv - i;
                while(l1 > 0 && l2 > 0){
                    if(h1->val < h2->val){
                        pre->next = h1;
                        h1 = h1->next;
                        --l1;
                    }
                    else{
                        pre->next = h2;
                        h2 = h2->next;
                        --l2;
                    }
                    pre = pre->next;
                }
                while(l1){
                    pre->next = h1;
                    pre = pre->next;
                    h1 = h1->next;
                    --l1;
                }
                while(l2){
                    pre->next = h2;
                    pre = pre->next;
                    h2 = h2->next;
                    --l2;
                }      
                // pre->next = (l1 == 0) ? h2 : h1;
                // while (l1 > 0 || l2 > 0) {
                //     pre = pre->next;
                //     l1--;
                //     l2--;
                // }
                pre->next = p;//將已排好序的那一步分和後面的連接上
            }
            intv *= 2;
        }
        return res->next;
    }
};
發佈了253 篇原創文章 · 獲贊 9 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章