排序算法学习之归并排序(2-路归并排序)

归并排序

基本思路:

假设初始序列有n个记录,则可看成是n个有序的子序列,每个序列长度为1,然后两两归并,得到\left \lceil n/2 \right \rceil个长度为2或者是1的有序子序列;再两两归并,,,,如此重复,直到得到一个长度为n的有序序列为止,这种排序方法称为2-路归并排序。

详细动态图解(详细图解)-------号外:归并排序发明者竟然是约翰-冯诺伊曼


归并操作的工作原理如下:

第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置

第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

重复步骤3直到某一指针超出序列尾

将另一序列剩下的所有元素直接复制到合并序列尾


//前子序列为从start到index,后子序列从index+1到end,现在需要将2个有序子序列进行归并成一个有序序列
void Merge(SqList *L,int start,int index,int end)
{
    int n=end-start+1;//共有n=end-start+1个记录
    int *S=(int*)malloc(n*sizeof(int));//生产n个记录大小辅助空间
    if(!S) exit(-1);//堆分配失败
    int i=start,j=index+1,k=0;//初始化指针,k=0为辅助数组指针,i指向前子序列,j指向后子序列
    for(;i<=index&&j<=end;k++){
        if(L->r[i]<=L->r[j]) S[k]=L->r[i++];//选择相对小的元素放入到辅助空间,指针后移一个位置
        else    S[k]=L->r[j++];
    }//直到某一指针超出子序列尾部
    if(i<=index)//将另一序列剩下的所有元素复制到辅助数组
        while(k<n&&i<=index)  S[k++]=L->r[i++];
    if(j<=end)
        while(k<n&&j<=end)    S[k++]=L->r[j++];
    for(i=start,k=0;i<=end&&k<n;i++,k++)
        L->r[i]=S[k];//将排序完成的辅助数组再赋值回SqList->r使之有序
    return;
}
void MergeSort(SqList *L)
{
    MSort(L,0,L->length-1);//序列从0到L->length-1,共L->length个记录关键字
    return;
}
void MSort(SqList *L,int i,int j)
{
    if(i<j)//如果i<j,则继续拆分,若i=j,则已经为单个元素序列,已经有序,不继续拆分
    {
        int m=(i+j)/2;//将头为i,尾为j的序列进行拆分
        MSort(L,i,m);//对前子序列进行递归
        MSort(L,m+1,j);//对后子序列进行递归
        Merge(L,i,m,j);//对已经有序的前序列和后序列进行归并,成为新的有序序列
    }
    return;
}

时间复杂度:

一趟归并排序的操作是,调用\left \lceil n/2h\right \rceil次Merge将前后相邻且长度为h的有序段进行两两归并,得到前后相邻,长度为2h的有序段

整个归并排序需要\left \lceil log_2{n} \right \rceil趟,故时间复杂度为O(nlogn)

空间复杂度:

实现归并排序需要和待排记录等数量的辅助空间,所以时间复杂度为O(n)

归并排序为一种稳定排序,对于相等关键字不改变其记录相对位置

posted on 2019-10-10 20:43  偷影子的人儿  阅读(8)  评论(0编辑  收藏  举报