归并排序链表的实现——学习笔记

归并排序是效率非常高的一种排序方式,和快速排序一样用了分治的思想。分治法的精髓在于将一个复杂问题分割成为多个简单的子问题,然后将子问题逐个解决,最终合并在一起以后就是复杂问题的解了。

 

这篇文章主要讲归并排序链表的实现方式,如果想了解数组的归并排序,可移步这里

归并排序的思想其实挺简单的,简而言之就是将数列分割成子序列,先对子序列进行排序,再将排好序的子序列合并成一个完整的数列。具体例子如下:

                       [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;
}

增加个C++版本的完整实现:

#include <iostream>

using namespace std;

//链表结构体
struct List{
    int val;
    List * next;
    
    List(int value = 0):
    val(value),
    next(nullptr)
    {
    }
};

//创建链表
List * createList(int length){
    List *root = new List(rand()%100);
    List *node = root;
    for(int i=0;i<length;++i){
        node->next = new List(rand()%100);
        node = node->next;
    }
    return root;
}

//打印链表
void printList(List * root){
    while(root!=nullptr){
        cout<<root->val<<" ";
        root = root->next;
    }
    cout<<endl;
}

//删除链表
void deletList(List * root){
    List * temp;
    while(root!=nullptr){
        temp = root->next;
        delete root;
        root = temp;
    }
}

//合并链表
List * merge(List * left,List * right){
    if(left == nullptr)
        return right;
    
    if(right == nullptr)
        return left;
    
    if(left->val < right->val){
        left->next = merge(left->next,right);
        return left;
    }else{
        right->next = merge(left,right->next);
        return right;
    }
}

//归并排序
List * mergeSort(List * root){
    //空链表或者长度为1的链表直接返回
    if(root == nullptr || root->next == nullptr)
        return root;
    
    //快慢指针寻找链表中间节点
    List *fast = root->next;
    List *slow = root;
    while(fast!=nullptr&&fast->next!=nullptr){
        slow = slow->next;
        fast = fast->next->next;
    }
    
    //将链表分成左右两段,分别进行归并排序
    List * right = mergeSort(slow->next);
    slow->next = nullptr;
    List * left = mergeSort(root);
    
    //合并左右链表
    return merge(left, right);
}

int main(){
    srand((unsigned)time(0));
    
    List * root = createList(10);
    
    printList(root);
    root = mergeSort(root);
    printList(root);
    
    deletList(root);
    return 0;
}

 

  • 16
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值