1. 程式人生 > >歸併排序連結串列的實現——學習筆記

歸併排序連結串列的實現——學習筆記

歸併排序是效率非常高的一種排序方式,和快速排序一樣用了分治的思想。分治法的精髓在於將一個複雜問題分割成為多個簡單的子問題,然後將子問題逐個解決,最終合併在一起以後就是複雜問題的解了。

這篇文章主要講歸併排序連結串列的實現方式,如果想了解陣列的歸併排序,可移步這裡

歸併排序的思想其實挺簡單的,簡而言之就是將數列分割成子序列,先對子序列進行排序,再將排好序的子序列合併成一個完整的數列。具體例子如下:

                       [3   7   6   4   8   9   2   1] 
                           /                  \
分割                 [3  7  6  4]          [8  9  2  1]
                     /          \          /        \
分割              [3   7]   [6   4]      [8   9]   [2   1]
                 /     \    /     \     /     \   /     \
分割            [3]    [7] [6]    [4]  [8]    [9] [2]    [1]
                 \     /   \     /     \     /    \     /
合併              [3   7]   [4   6]      [8   9]   [1   2]
                     \        /             \        /
合併                 [3  4  6  7]          [1  2  8  9]
                          \                     /
合併                   [1   2   3   4   6   7   8   9]

分割時從數列中間開始,將數列分成兩部分,然後對分割後的序列繼續進行分割直到分割不能再分為止。然後對已經排好序的子序列進行合併,重新產生一個排序好的數列。

用連結串列進行歸併排序和陣列排序略有不同,但是思想還是一樣的。唯一的難點在於如何找到連結串列的中間點,這裡就要運用到了一個額外的技巧——快慢指標。快慢指標常常用於判斷連結串列中是否有環,簡單來說就是有兩個指標,快指標每次走兩個節點,慢指標每次走一個節點,當快指標走到連結串列盡頭時,慢指標才走到了連結串列的一半,此時就是我們想要的中間節點。

看程式碼:

//連結串列結構單位
struct Node{
    int val;
    Node* next;
};

//node -- 連結串列表頭
Node* MergeSort(Node* node){
    //先判斷連結串列長度是否大於1,小於1時無須排序
    if(node!=NULL&&node->next!=NULL){
        //運用快慢指標,找到連結串列的中間節點
        Node *fast=node->next;
        Node *slow=node;
        while(fast!=NULL&&fast->next!=NULL){
            fast=fast->next->next;
            slow=slow->next;
        }

        //將連結串列分成兩部分進行分割
        Node *p1=MergeSort(slow->next);
        slow->next=NULL;                 //這兒很重要,仔細想想為什麼
        Node *p2=MergeSort(node);

        //對兩條子鏈進行歸併
        Node *p0=(Node *)malloc(sizeof(Node));
        Node *p=p0;
        while(p1!=NULL&&p2!=NULL){
            if(p1->val<p2->val){
                p->next=p1;
                p1=p1->next;
            }else{
                p->next=p2;
                p2=p2->next;
            }
            p=p->next;
        }

        if(p1!=NULL){
            p->next=p1;
        }

        if(p2!=NULL){
            p->next=p2;
        }

        p=p0->next;
        free(p0);
        return p;
    }

    return node;
}