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


    正文之前

    归并排序(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. 稳定性

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

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

  • 相关阅读:
    C_数据结构_栈
    C_数据结构_链表
    C_数据结构_数组的修改和删除
    C_数据结构_数组
    Python_闭包_27
    Python_函数的镶嵌和作用域链_26
    P1428 小鱼比可爱
    P2915 [USACO08NOV]奶牛混合起来Mixed Up Cows
    P2946 [USACO09MAR]牛飞盘队Cow Frisbee Team
    codevs 2173 忠诚
  • 原文地址:https://www.cnblogs.com/lihanxiang/p/8456908.html
Copyright © 2020-2023  润新知