归并排序-自顶向下/自底向上


author: i.sshe
email: [email protected]
github: https://github.com/isshe


归并排序[二路]

  1. 归并排序是将要排序数组递归地分成两半分别排序,然后将结果合并起来,完成排序.
  2. 归并排序是最理想的直接排序算法(运行时间与NlogN成正比),执行过程具有稳定性.
  3. 归并排序可以处理数百万甚至更大规模的数组, 这是插入排序或选择排序做不到的.

  4. 归并排序示意图:
    归并排序-自顶向下/自底向上_第1张图片
      1). 把前面部分元素和后面部分元素比较,小的放入结果数组.(注意此处的前半部分/后半部分已经排好序了)

  5. 可以把后半部分倒序, 两部分数组中最大的元素充当观察哨, 这样可以减少判断.
    代码可以从

    归并排序-自顶向下/自底向上_第2张图片

    变为

    归并排序-自顶向下/自底向上_第3张图片

  6. 合并代码
void merge(int *a, int *b, int lo, int mid, int hi)
{
     int    i = lo;
     int    j = mid + 1;
     int    k = 0;

     //copy a[lo..hi] to b[lo..hi].
     for (k = lo; k <= hi; k++)
     {
         b[k] = a[k];
     }

     for (k = lo; k <= hi; k++)
     {
          if (i > mid)          //到达边缘
          {
              a[k] = b[j];
              j++;
          }
          else if (j > hi)    //到达边缘
          {
              a[k] = b[i];
              i++;
          }
          else if (b[i] > b[j])
          {
               a[k] = b[j];
               j++;
          }
          else          //b[i] < b[j]
          {
               a[k] = b[i];
               i++;
          }
     }
}

自顶向下的归并排序

  1. 轨迹图
    归并排序-自顶向下/自底向上_第4张图片
      1). 排序E,M, 排序G,R,
      2). 合并在一起.
      3). 以此反复.
  2. 依赖树
    归并排序-自顶向下/自底向上_第5张图片
  3. 代码
void top_down_merge_sort(int *a, int *b, int lo, int hi)
{
    if(lo >= hi)
    {
         return ;
    }

    int mid = (lo + hi) / 2;        //
    top_down_merge_sort(a, b, lo, mid);
    top_down_merge_sort(a, b, mid+1, hi);
    merge(a, b, lo, mid, hi);
}

改进

  1. 对小规模子数组(长度小于15左右)使用插入排序, 可以缩短10%-15%的运行时间.
  2. 测试数组是否有序:添加一个判断条件, 如果a[mid]<=a[mid+1],则认为数组已经有序并跳过merge()方法.(<<算法 第四版>>p175页,不懂)
  3. 不将元素复制到辅助数组: 要做到这一点需要调用两种排序方法, 一种将数据从输入数组排序到辅助数组, 一种将数据从辅助数组排序到输入数组.(<<算法 第四版>>p175页,不懂)

自底向上的归并排序

归并排序的另一种方法是: 两两归并子数组.
1. 轨迹图
归并排序-自顶向下/自底向上_第6张图片
注意:sz=1中, 奇数位置的总比偶数位置的短!
2. 归并结果图:
归并排序-自顶向下/自底向上_第7张图片
3. 代码

void bottom_up_merge_sort(int *a, int *b, int len)
{
     int    sz = 0;             //size:间隔:2, 4, 8...
     int    lo = 0;
     int    hi = 0;
     int    N = len;

     for (sz = 1; sz < N; sz = sz + sz)       //1+1=2, 2+2=4...控制间隔
     {
         for (lo = 0; lo < N - sz; lo += sz + sz)      //控制次数
         {
             hi = (lo + sz + sz -1) < (N - 1) ? (lo + sz + sz -1) : (N -1);
              merge(a, b, lo, lo+sz-1, hi);
         }
     }
}

3. 自底向上的归并排序适合用于链表组织的数据.

归并排序告诉我们:当能够用其中一种方法解决一个问题的时候,你都应该试试另一种!

参考资料:
1. <<算法第四版>>
2. <<算法:C语言实现1-4部分>>

你可能感兴趣的:(【初探】数据结构与算法)