3.3 分治模式的完美诠释:归并排序

Chapter3: 更好的查找与排序算法

3. 分治模式的完美诠释:归并排序

1. 基本思想

对于一次递归的调用来说

  • 划分

    • 取中间坐标,将原数组划分为两部分
    • 分别递归对左右数组进行排序,该层调用的结果为左边数组有序,右边数组有序,但是左边数组的元素不一定比右边数组的小,如 [1,3,4,6,2,4,6,8]
  • 合并

    • 比如对于数组arr=[1,5,3,7,2,6,8,4] 进行一次归并排序调用(包括划分和左右数组排序)后,得到左数组 arr1=[1,3,5,7] ,右数组 arr2=[2,4,6,8]
    • 初始化: 让 left 指针指向做数组第一个元素,right 数组指向右数组第一个元素,设置一个辅助数组 tmpArr=int [8] ;
    • arr1[left]arr2[right] 比较,arr1[left]=1arr2[right]=2 小,将 arr1[left] 存入 tmpArr[0] ; arr1[left]指针下移1位,即left++, 此时 arr1[left]=3arr2[right]=2大,则将arr2[right] 存入tmpArr[1]; right++...循环直至其中一个数组的元素全部存入临时数组,另一个数组剩下的元素直接赋值到临时数组后面
7346071-e1e40674360e3445.png
归并排序示意图
7346071-93ac4f7ca4e377b5.png
某层递归调用上的合并示意图

2. 代码实现

#include<iostream>
#include<cstdlib>
using namespace std;

void mergeSort(int* arr,int begin,int end); 
void merge(int* arr,int begin,int mid,int end);

int main(){
    int arr[6]={1,3,2,5,4,7};
    int arrLen=sizeof(arr)/sizeof(arr[0]);
    
    mergeSort(arr,0,arrLen-1);
    
    for(int i=0;i<arrLen;i++){
        printf("%d ",arr[i]);
    }
    return 0;
} 

void mergeSort(int* arr,int begin,int end){
    if(begin<end){//注意这个出口条件,不然会陷入死循环 
        int mid=begin+((end-begin)>>1);
        mergeSort(arr,begin,mid);//递归调用起到划分作用
        mergeSort(arr,mid+1,end);
        merge(arr,begin,mid,end);
    }
}

//合并函数
void merge(int* arr,int begin,int mid,int end){
    int len=end-begin+1;//当前划分状态下的数组长度 
    int tmpArr[len];//辅助数组
     
    int leftP=begin;//左指针 
    int rightP=mid+1;//右指针 
    int i=0;//辅助数组当前存取的下标 
    //printf("initial. len=%d\n",len);
    
    while((leftP<=mid)&&(rightP<=end)){
        if(arr[leftP]<=arr[rightP]){
            tmpArr[i]=arr[leftP];
            i++;
            leftP++;
            //或直接写为 tmpArr[i++]=arr[leftP++];
        }
        else{
            tmpArr[i]=arr[rightP];
            i++;
            rightP++; 
        }
    }
    while(leftP<=mid){//如果左数组有剩余元素 
        tmpArr[i]=arr[leftP];
        i++; 
        leftP++;
    }
    while(rightP<=end){//如果右数组有剩余元素 
        tmpArr[i]=arr[rightP];
        i++;
        rightP++;
    }
    /*将排序好的辅助数组复制到原数组中,
    因为递归要用到上一层排好序的原数组,并所以这一步必不可少*/ 
    i=0;  
    while(begin<=end){
        arr[begin++]=tmpArr[i++];
    }
    //注意每次递归数组的开始位置不一样,为begin,不一定是0,所以不能用下面的赋值方法 
//  for(i=0;i<len;i++){
//      arr[i]=tmpArr[i];
//  }       

}

3. 快速排序和归并排序的比较

快速排序关键在于划分,归并排序关键在于合并

快速排序划分的左右两个数组左边数组的元素一定小于右边数组的元素,但是在左数组或右数组内元素不一定有序

归并排序划分的左右两个数组左边的数组的元素不一定小于右边的元素,但是在左右数组内的元素分别是有序的

快速排序的思想
  • 分解

    数组 A[p..r] 被划分为两个子数组 A[p...q-1]A[q+1,r] , 使得 A[q] 为大小居中的数,左侧 A[p..q-1] 中的每个元素都 <= 它, 而右侧 A[q+1,r] 中的每个元素都 >= 它。其中计算下标 q 也是划分过程的一部分。

  • 解决

    通过递归调用快速排序,分别对子数组 A[p...q-1] 和 A[q...r] 进行排序

  • 合并

    不存在合并的问题

4. 参考资料

[1] 图解排序算法(四)之归并排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值