打赏

数据结构与算法系列——排序(10)_归并排序

1. 工作原理(定义)

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法,指的是将两个已经排序的序列合并成一个序列的操作。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:

  • 自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
  • 自下而上的迭代;

2. 算法步骤

1. 从下往上的归并排序【迭代】:

  1. 通过分治法将长度为n的序列划分为n个长度为1的子序列。 

  2. 进行两两归并比较,得到 n/2 个长度为 2 的有序子序列 

  3. 重复第 2 步,直到所有子序列归并成一个长度为 n 的有序序列。

2. 从上往下的归并排序【递归】:

  1. 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2; 

  2. 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。

  3. 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]。

3. 归并步骤:

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

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

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

  4. 重复步骤 3 直到某一指针达到序列尾;

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

  

 

3. 动画演示

  

  这里写图片描述 

 

4. 性能分析

1. 时间复杂度

  归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要 logN 步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN),故时间复杂度为O(nlogn)。

2. 空间复杂度

  归并排序过程中,需要一个辅助空间来暂存两有序子文件归并的结果,因此空间复杂度为O(n)。

3. 算法稳定性 

  归并是稳定的算法,在分解和并归过程中元素相对顺序未发生改变。

4. 初始顺序状态

  1. 比较次数:
  2. 移动次数:
  3. 复杂度:    
  4. 排序趟数:

5. 归位

  不能归位

6. 优点

  1. 排序:速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列
  2. 求逆序对数:在归并的过程中计算每个小区间的逆序对数,进而计算出大区间的逆序对数(也可以用树状数组来求解)

7. 具体代码

public class MergeSort {

    // 归并排序(从上往下,递归)
    public static void mergeSortUp2Down(int [] arr, int start, int end){
        if(arr!=null && start<end){//当子序列中只有一个元素时结束递归
            int mid=(start+end)/2;//划分子序列
            mergeSortUp2Down(arr, start, mid);//对左侧子序列进行递归排序
            mergeSortUp2Down(arr, mid+1, end);//对右侧子序列进行递归排序
            merge(arr, start, mid, end);//合并
        }
    }
    
    //归并排序(从下往上,迭代)
    public static void mergeSortDown2Up(int [] arr){
        if(arr!=null){
            int len = arr.length;
            //gap表示有序数组的长度(1,2,4,8……)
            for(int gap = 1; gap < len; gap*=2){
                int i;
                // 将"每2个相邻的子数组" 进行合并排序。
                for(i = 0; i+2*gap-1 < len; i+=(2*gap)){
                    merge(arr, i, i+gap-1, i+2*gap-1);
                }
                // 若 i+gap-1 < len-1,则剩余一个子数组没有配对。
                // 将该子数组合并到已排序的数组中。
                if (i+gap-1 < len-1){
                    merge(arr, i, i+gap-1, len-1);
                }
                
            }
        }
    }
    
    //两路归并算法,两个排好序的子序列合并为一个子序列
    public static void merge(int []arr, int left, int mid, int right){
        int []tmp=new int[arr.length];//辅助数组
        int p1=left,p2=mid+1,k=left;//p1、p2是检测指针,k是存放指针

        while(p1<=mid && p2<=right){
            if(arr[p1]<=arr[p2])
                tmp[k++]=arr[p1++];
            else
                tmp[k++]=arr[p2++];
        }

        while(p1<=mid) tmp[k++]=arr[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
        while(p2<=right) tmp[k++]=arr[p2++];//同上

        //复制回原素组
        for (int i = left; i <=right; i++) 
            arr[i]=tmp[i];
    }
    
    public static void main(String[] args){
        int[] arr = { 49, 38, 65, 97, 76, 13, 27, 50 };
        //mergeSortUp2Down(arr, 0, a.length-1);
        mergeSortDown2Up(arr);
        System.out.println("排好序的数组:");
        for (int e : arr)
            System.out.print(e+" ");
    }
}

8. 参考网址

  1. 数据结构基础学习笔记目录
  2. 排序算法系列之并归排序
  3. 图解排序算法(四)之归并排序
  4. https://visualgo.net/en/sorting
  5. https://www.runoob.com/w3cnote/merge-sort.html
  6. https://github.com/hustcc/JS-Sorting-Algorithm
  7. 归并排序
  8. Java实现归并排序-有图有真相
posted @ 2019-05-23 16:37  海米傻傻  阅读(308)  评论(0编辑  收藏  举报