[排序/C++]5. 归并排序

5. 归并排序

5.1.1 算法分析和思路

  1. 基本思想
    采用分治法(Divide and Conquer)的一个典型应用
    将两个或两个以上的有序子序列归并为一个有序序列。

    1. 把长度为n的输入序列分成两个长度为n/2的子序列;
    2. 对这两个子序列分别采用归并排序;
    3. 将两个排序好的子序列合并成一个最终的排序序列。
  2. 2-路归并排序
    在内部排序中,通常采用的是2-路归并排序
    将两个位置相邻的有序子序列L[l…m]和L[m+1…n]归并为一个有序序列L[l…n],即将两个有序子序列归并成一个有序序列

5.1.2 算法示例

归并排序示例

归并排序画出来像一个倒着的树,称之为“归并树”
归并需要的趟数,即“归并树”的高度: l o g 2 n log_2 n log2n
整个归并排序仅需logn趟

  • 关键问题:如何将两个有序序列合成一个有序序列?
    如何将两个有序序列合成一个有序序列?
    1. 增加一个空间为两个有序序列之和的辅助空间;
    2. 两个指针分别指向两个有序序列第一个元素;
    3. 比较指针指向的元素大小,较小者放入辅助空间,并移动子序列和辅助空间的指针;
    4. 重复步骤3,直至合并为一个有序序列。

5.1.3 算法代码

//归并排序算法
void MergeSort(SqList &L){
    int len = 1;
    SqList tempL;
    InitList_Sq(tempL);//新建辅助顺序表tempL
    SqList * LP = &L;//初始化LP指针指向顺序表L
    SqList * tempLP = &tempL;//初始化tempLP指针指向顺序表tempL
    SqList * tempP;//交换指针时的临时指针
    while(len < L.length) {
        int s = len;
        len = 2 * s;
        int i;
        for(i = 1; i+len <= L.length; i = i + len){//有哨兵位,索引从1开始
            Merge(*LP, *tempLP,  i, i+s-1, i+len-1 );//对等长的两个子表合并
        }
        if(i+s <= L.length){
            Merge(*LP, *tempLP,  i, i+s-1, L.length);//对不等长的两个子表合并
        }
        else{
            //剩余且无需归并的记录依次赋值到辅助顺序表对应位置
            for(; i <= L.length; i++){
                tempLP->nums[i] = LP->nums[i];
                // (*tempLP).nums[i] = (*LP).nums[i];
            }
        }
        //交换LP和tempLP,以保证下一趟归并时,仍从LP归并到tempLP
        tempP = LP; LP = tempLP; tempLP = tempP;
    }
    /*
        最后判断排序好的顺序表是否是L即可
        LP指向排好序的顺序表,只需判断LP是否指向L即可
        若LP不指向L,即L并非是最终有序,则需要把有序表中的记录依次赋值到L对应位置上
    */
    if(&L != LP){
        for(int i = 1; i <= L.length; i++){
            L.nums[i] = (*LP).nums[i];
        }
    }
}

//归并操作:将两个有序序列合成一个有序序列
//将L[low...m]和L[m+1...high]归并到辅助空间temp[low...high]
void Merge(SqList &L, SqList &temp, int low, int m, int high){
    int i = low;//i指向第一个子序列的第一个元素
    int j = m+1;//j指向第二个子序列的第一个元素
    int k = low;//k指向辅助空间里对应第一个子序列第一个元素的位置
    while((i <= m) && (j <= high)){
        if(L.nums[i].key <= L.nums[j].key){
            temp.nums[k++] = L.nums[i++];
        }else{
            temp.nums[k++] = L.nums[j++];
        }
        // temp.nums[k++] = (L.nums[i].key <= L.nums[j].key) ? L.nums[i++] : L.nums[j++];
    }
    //若其中一个子序列没有元素时,剩下的一个子序列的剩余元素全部直接搬到辅助空间
    while(i <= m){
        temp.nums[k++] = L.nums[i++];
    }
    while(j <= high){
        temp.nums[k++] = L.nums[j++];
    }
}

排序算法详细代码

5.1.4 算法性能分析

  • 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
    • 所有元素最后都要合并一次是n,总共需要合并logn,所以总共是nlogn
  • 空间复杂度: O ( n ) O(n) O(n)
    • 需要一个与原始序列同样大小的辅助序列,这正是此算法的缺点。
  • 是一种稳定的排序方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值