归并排序,合并合并合并再合并!

77 阅读2分钟

归并排序理念

归并排序,也叫多路归并排序,这里使用二路归并排序,准确来说几路就是分成几个子数组并合并。

归并排序就是将一个数组一直从中点分割一直到一个元素,直到一个元素的时候,一个元素的时候为有序。合并过程把两个子数组变得有序。

image.png

归并排序的步骤

1、求中间元素下标。

2、当不止一个元素的时候归并排序左数组和右数组。

3、合并左右数组,合并过程对所有元素进行排序。

合并合并的图解

以上面分割好的例子进行合并,这里对于树的操作就使用递归进行: 绿色的线表示对应的操作在合并的第几步。

image.png

归并排序的代码

//合并,arr--数组,left~mid左数组,mid+1~right右数组 
void merge(int arr[],int left,int mid,int right){
    //左右数组的长度
    int leftN = mid-left+1;
    int rightN = right-mid;
    
    //辅助数组左右数组
    int L[leftN], R[rightN];
    
    //给左右数组赋值
    for(int i=0;i<leftN;i++){
        L[i] = arr[left+i];
    } 
    for(int i=0;i<rightN;i++){
        R[i] = arr[mid+1+i];
    }
    
    //左右指针以及传入的数组最开始下标 
    int i=0,j=0,k=left; 
    
    /*
        移动找最小值放到原数组,当一边数组遍历完成退出循环 
        由于在mergeSort函数中每次合并的左右数组必定是已经递归排序好的数组
        所以,只需要左右数组从头开始遍历并且对比移动拿到对应的最小值就好了 
    */ 
    while(i<leftN&&j<rightN){
        if(L[i]<=R[j]){
            arr[k++] = L[i++];//选左数组对应元素 
        }else{
            arr[k++] = R[j++];//选右数组对应元素 
        }   
    }
    
    //由于提前退出所以左右数组必定有一个没遍历完直接放在最后就行
    while(i<leftN) arr[k++] = L[i++]; 
    while(j<rightN) arr[k++] = R[j++];  
     
} 
​
void mergeSort(int arr[],int left,int right){
    //当left<right时进行递归合并
    if(left<right){
        //防止溢出所以不用int mid=(right+left)/2; 
        int mid = left+(right-left)/2;
        
        //递归左右数组直到一个元素必有序 
        mergeSort(arr,left,mid);
        mergeSort(arr,mid+1,right);
        
        //合并
        merge(arr,left,mid,right); 
        
    } 
}
​

总结

  • 归并排序的时间复杂度呢为O(nlogn)

    • 产生的树的高度为logn,每层合并的总操作为n、
  • 空间复杂度为O(n)

    • 递归使用了O(logn)的栈空间
    • 合并使用了O(n)的辅助数组空间
  • 排序是稳定排序,因为在合并时候操作L[i]<=R[j]这段代码使两个相同的数不会调换相对位置