【算法】排序(四)归并排序

正文之前

归并排序(Merge sort),是建立在归并操作上的一种有效的排序算法,效率为O(n logn),1945年由约翰诺依曼首次提出,该算法是采用分治法的一个非常典型的应用,且各层分治递归可以同时进行。
                        ——Wikipedia


本文将介绍以下内容

排序原理
算法实现(JAVA)
测试阶段
算法分析

正文

排序原理

采用分治的做法,将一个数组分为两个序列,将序列继续拆分达到最小,分别排序,最后将有序的序列归并,达到全部有序的效果。

算法实现

1. 归并操作
public class MergeSort {
    private static int[] temp;              //临时数组

    public static void merge(int[] a, int low, int mid, int high){
        int i = low;                        //第一序列首
        int j = mid + 1;                    //第二序列首
        for (int k = low; k <= high ; k++) {
            temp[k] = a[k];                 //复制元素
        }
        for (int k = low; k <= high; k++) {
            if(i > mid){                    //第一序列的全部元素已归并
                a[k] = temp[j++];           
            }
            else if(j > high){              //第二序列的全部元素已归并
                a[k] = temp[i++];           
            }
            else if(temp[j] < temp[i]){     //第二序列的元素小于第一序列元素
                a[k] = temp[j++];
            }
            else{
                a[k] = temp[i++];           //第一序列的元素小于第二序列
            }
        }
    }
}

归并的操作就是依次从序列中选取元素,两个序列中的元素比较,哪个小选哪个,一个序列全部选完了,就直接选择另一个序列的全部元素

2. 排序操作
public static void sort(int[] a){
        temp = new int[a.length];
        sort(a, 0, a.length - 1);                      //排序整个数组
    }
    private static void sort(int[] a, int low, int high){
        if(low >= high){
            return;
        }
        int mid = low + (high - low) / 2;
        sort(a, low, mid);                              //排序左半边  
        sort(a, mid + 1, high);                         //排序右半边
        merge(a, low, mid, high);                       //归并
    }

sort 方法的作用就是递归执行自己,直到将序列拆分至最小,然后用 merge 方法来排序,最后归并

整个排序过程的代码块就是如此:
public class MergeSort {
    private static int[] temp;              //临时数组

    private static void merge(int[] a, int low, int mid, int high){
        int i = low;                        //第一序列首
        int j = mid + 1;                    //第二序列首
        for (int k = low; k <= high ; k++) {
            temp[k] = a[k];                 //复制元素
        }
        for (int k = low; k <= high; k++) {
            if(i > mid){                    //第一序列的全部元素已归并
                a[k] = temp[j++];
            }
            else if(j > high){              //第二序列的全部元素已归并
                a[k] = temp[i++];
            }
            else if(temp[j] < temp[i]){     //第二序列的元素小于第一序列元素
                a[k] = temp[j++];
            }
            else{
                a[k] = temp[i++];           //第一序列的元素小于第二序列
            }
        }
    }
    
    private static void sort(int[] a){
        temp = new int[a.length];
        sort(a, 0, a.length - 1);                      //排序整个数组
    }
    
    private static void sort(int[] a, int low, int high){
        if(low >= high){
            return;
        }
        int mid = low + (high - low) / 2;
        sort(a, low, mid);                              //排序左半边
        sort(a, mid + 1, high);                         //排序右半边
        merge(a, low, mid, high);                       //归并
    }
}
测试阶段
public static void main(String[] args){
        int[] a = new int[10];
        for (int i = 0; i < 10; i++) {
            a[i] = (int)(Math.random() * 100);
            System.out.print(a[i] + " ");
        }
        System.out.println();
        sort(a);
        for (int i = 0; i < 10; i++) {
            System.out.print(a[i] + " ");
        }
    }

生成十个随机数字,并调用排序方法,接下来选取几组测试结果:

算法分析

1. 特点:

归并排序将所有元素复制到一个辅助数组中,将归并后的结果放入原数组中,不需要额外的空间

2. 时间复杂度

这里需要用一些数学公式:

  1. 设数组的长度为N,用 T(N) 表示排序一个长度为N的数组所需要的比较次数,可想而知,T(0) 和 T(1) 为0

  2. 将数组的两边分别排序各需要 N/2 次比较(上限),归并所需次数为N(上限)

  3. 所以 T(N) ≤ T(N/2) + T(N/2) + N

  4. 设 N = 2n且不等式的等号成立:
    T(2n) = 2T(2n-1) + 2n
    (N / 2 = 2n - 1)

  5. 等式两边同时除以2n,得到:
    T(2n) / 2n = T(2n-1) / 2n - 1 + 1

  6. 由上式可以得到:
    T(2n - 1) / 2n - 1 = T(2n-2) / 2n - 2 + 1

  7. 将 6 的结果代入 5 中,得到:
    T(2n) / 2n = T(2n-2) / 2n - 2 + 1 + 1

  8. 重复第7步,直到得到如下结果:
    T(2n) / 2n = T(20)/20 + n
    (共计 n - 1 步)

  9. 等式两边同时乘以 2n ,得到结果:
    T(N) = 2n T(20) + n 2n = NlogN
    ( n = logN)

所以,算法复杂度为O(nlogn)

3. 稳定性

当两个元素相等时,归并排序不会将两者交换,所以归并排序是稳定的

关于归并排序的介绍就到此了,谢谢!

posted @ 2018-02-21 17:12  李汉祥  阅读(278)  评论(0编辑  收藏  举报