• 归并排序


    1. 归并排序

    1.1 排序原理

    • 采用分治思想,一般用递归实现
    • 将一个无序数组从中间分成两个无序数组,然后对两个无序数组排序,排序之后再将两个有序数组合并为一个有序数组;

    1.2 性能分析

    1.2.1 执行效率

      它的效率和原始数组的元素顺序无关,效率稳定;最好,最坏,平均复杂度都一样;

      假设n个元素的排序时间为T(n),那么两个子数组的排序时间就是2*T(n/2),合并两个子数组的时间是O(n);

      则T(n)= 2*T(n/2)+ n ;继续分解如下

    T(n) = 2*T(n/2) + n
         = 2*(2*T(n/4) + n/2) + n = 4*T(n/4) + 2*n
         = 4*(2*T(n/8) + n/4) + 2*n = 8*T(n/8) + 3*n
         = 8*(2*T(n/16) + n/8) + 3*n = 16*T(n/16) + 4*n
         ......
         = 2^k * T(n/2^k) + k * n
         ......

      T(n/2^k)最小时等于T(1);即 n/2^k = 1 时,k = log2n, 带入上述公式,

      T(n)=Cn+nlog2n,

      用大O表示法表示为T(n)=O( n log n);

    1.2.2  空间复杂度

      每次合并数组时需要申请额外空间,但是每次合并完后都会释放内存,最多申请N个元素的空间,所以空间复杂度是O(n),所以不是原地排序算法。

    1.2.3 算法稳定性 

      在合并两个较小的数组时,可以元素相等时优先将前面的元素拷贝到临时数组,这样就可以保证稳定性。

    1.3 代码实现

    // 递归调用函数
          private static void mergeSortInternally(int[] a, int start, int end) {
            // 递归终止条件
            if (start >= end) return;
    
            // 取start到end之间的中间位置mid,防止(start+end)的和超过int类型最大值
            int mid = start + (end - start)/2;
            // 分治递归
            mergeSortInternally(a, start, mid);
            mergeSortInternally(a, mid+1, end);
    
            // 将a[start...mid]和A[mid+1...end]合并为A[start...end]
            merge(a, start, mid, end);
          }
    
          private static void merge(int[] a, int p, int q, int r) {
            int i = p;
            int j = q+1;
            int k = 0; // 初始化变量i, j, k
            int[] tmp = new int[r-p+1]; // 申请一个大小跟a[p...r]一样的临时数组
            while (i<=q && j<=r) {
    
              if (a[i] <= a[j]) {    
                tmp[k++] = a[i++];  
              } else {
                tmp[k++] = a[j++];    
              }
              
              // 当一个数组中的元素全部拷贝到tmp中时,就将另一个数组剩余的元素也全部拷贝到tmp中
              if (i == (q+1)){
                while(j<=r){
                    tmp[k++] = a[j++];
                }
              }
              
              if (j == (r+1)){
                while(i<=q){
                    tmp[k++] = a[i++];
                }
              }
              
            }
    
    //        // 判断哪个子数组中有剩余的数据
    //        int start = i;
    //        int end = q;
    //        if (j <= r) {
    //          start = j;
    //          end = r;
    //        }
    //
    //        // 将剩余的数据拷贝到临时数组tmp
    //        while (start <= end) {
    //          tmp[k++] = a[start++];
    //        }
    
            // 将tmp中的数组拷贝回a[p...r]
            for (i = 0; i <= r-p; ++i) {
              a[p+i] = tmp[i];
            }
          }
  • 相关阅读:
    工作也是一样,认真对待,你是在为自己工作
    程序员学习能力提升三要素(转载)
    该读些啥书
    每个程序员都应读的书
    微博时光机定时发送微博
    WordPress快速建站
    Tweenlite的用法
    Away3D粒子系统中文快速上手指南
    操盘手 李彪 照片[转]
    URLClassLoader加载class到当前线程类加载器【zt】
  • 原文地址:https://www.cnblogs.com/virgosnail/p/10486582.html
Copyright © 2020-2023  润新知