排序之归并排序的算法思想及实现代码


归并排序

归并排序的基本思想

归并排序与前面讲到的基于交换、选择等排序的思想不一样,“归并”的含义是将两个或两个以上的有序表组合成一个新的有序表

二路归并的基本思想
假定待排序表含有 n n n 个记录,可将其视为 n n n 个有序的子表,每个子表的长度为1,然后两两归并,得到 ⌈ n / 2 ⌉ \lceil n/2 \rceil n/2个长度为2或1的有序表;再两两归并……如此重读,直到合并成一个长度为 n n n 的有序表为止。
这种排序方法称为2路归并排序。

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

在这里插入图片描述
可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程,递归深度为 l o g 2 n log_2n log2n

再来看看阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。

在这里插入图片描述
在这里插入图片描述


归并排序的实现代码

归并排序的实现需要两个算法,一个是按 l e n len len做一趟两两归并,另一个是归并排序。

M e r g e ( ) Merge() Merge()的功能是将前后相邻的两个有序表归并为一个有序表。

 设两段有序表 L . d a t a [ l o w . . . m i d ] L.data[low...mid] L.data[low...mid] L . d a t a [ m i d + 1... h i g h ] L.data[mid+1...high] L.data[mid+1...high]存在放同一顺序表中的相邻位置,先将它们复制到辅助数组 B [ ] B[] B[]中。每次从对应B中的两个段取出一个记录进行关键字的比较,较小者放入 L . d a t a [ ] L.data[] L.data[]中,当数组 B [ ] B[] B[]中有一段的下标超出其对应的表长(即该段元素已经完全复制到 L . d a t a [ ] L.data[] L.data[]中)时,将另一端中的剩余部分直接复制到 L . d a t a [ ] L.data[] L.data[]中。

//合并
void Merge(SeqList &L, int low, int mid, int high){
    int B[L.n];  //辅助数组B
    int i,j,k;
    for(k=low; k<=high; k++)
        B[k] = L.data[k];  //将L.data中的所有元素复制到B中
    for(i=low, j=mid+1, k=i; i<=mid&&j<=high; k++){
        if(B[i] <= B[j])  //比较B的左右两段中的元素
            L.data[k] = B[i++];  //较小的值复制到L.data中
        else
            L.data[k] = B[j++];
    }
    while(i<=mid) L.data[k++] = B[i++];  //若第一个表未检测完,复制
    while(j<=high) L.data[k++] = B[j++];  //若第二个表未检测完,复制
}

归并排序(递归):

//归并排序
void MergeSort(SeqList &L, int low, int high){
    if(low<high){
        int mid = (low+high)/2;  //从中间划分两个子序列
        MergeSort(L, low, mid);  //对左侧子序列进行递归排序
        MergeSort(L, mid+1, high);  //对右侧子序列进行递归排序
        Merge(L, low, mid, high); //归并
    }
}

归并排序的性能分析

空间复杂度

由于辅助空间占用 n n n 个单元,故归并排序的空间复杂度为 O ( n ) O(n) O(n)

时间复杂度

每趟归并的时间复杂度为 O ( n ) O(n) O(n),总共需要进行 ⌈ l o g 2 n ⌉ \lceil log_2n \rceil log2n趟归并,所以算法的时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

稳定性

由于 M e r g e ( ) Merge() Merge()操作不会改变相同关键字记录的相对次序,故二路归并排序算法是一种稳定的排序方法


完整代码

#include<bits/stdc++.h>
using namespace std;

#define maxSize 20
typedef struct{
    int data[maxSize];
    int n;
}SeqList;

void PrintList(SeqList L){
    for(int i=0; i<L.n; i++){
        cout<<L.data[i]<<" ";
    }
    cout<<endl<<endl;
}

//合并
void Merge(SeqList &L, int low, int mid, int high){
    int B[L.n];  //辅助数组B
    int i,j,k;
    for(k=low; k<=high; k++)
        B[k] = L.data[k];  //将L.data中的所有元素复制到B中
    for(i=low, j=mid+1, k=i; i<=mid&&j<=high; k++){
        if(B[i] <= B[j])  //比较B的左右两段中的元素
            L.data[k] = B[i++];  //较小的值复制到L.data中
        else
            L.data[k] = B[j++];
    }
    while(i<=mid) L.data[k++] = B[i++];  //若第一个表未检测完,复制
    while(j<=high) L.data[k++] = B[j++];  //若第二个表未检测完,复制
}

//归并排序
void MergeSort(SeqList &L, int low, int high){
    if(low<high){
        int mid = (low+high)/2;  //从中间划分两个子序列
        MergeSort(L, low, mid);  //对左侧子序列进行递归排序
        MergeSort(L, mid+1, high);  //对右侧子序列进行递归排序
        Merge(L, low, mid, high); //归并
    }
}

int main(){
    SeqList L;
    int n;
    cin>>n;
    for(int i=0; i<n; i++){
        cin>>L.data[i];
    }
    L.n = n;
    cout<<endl<<"L:";
    PrintList(L);
    //归并排序
    MergeSort(L, 0, L.n-1);
    PrintList(L);
    return 0;
}

运行结果:
在这里插入图片描述


说明

文章图片及部分文字转自https://www.cnblogs.com/chengxiao/p/6194356.html
作者: dreamcatcher-cx
出处: http://www.cnblogs.com/chengxiao/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值