author: i.sshe
email: i.sshe@foxmail.com
github: https://github.com/isshe
归并排序[二路]
- 归并排序是将要排序数组递归地分成两半分别排序,然后将结果合并起来,完成排序.
- 归并排序是最理想的直接排序算法(运行时间与NlogN成正比),执行过程具有稳定性.
归并排序可以处理数百万甚至更大规模的数组, 这是插入排序或选择排序做不到的.
归并排序示意图:
1). 把前面部分元素和后面部分元素比较,小的放入结果数组.(注意此处的前半部分/后半部分已经排好序了)可以把后半部分倒序, 两部分数组中最大的元素充当观察哨, 这样可以减少判断.
代码可以从
…
…
变为
…
…- 合并代码
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). 排序E,M, 排序G,R,
2). 合并在一起.
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);
}
改进
- 对小规模子数组(长度小于15左右)使用插入排序, 可以缩短10%-15%的运行时间.
- 测试数组是否有序:添加一个判断条件, 如果a[mid]<=a[mid+1],则认为数组已经有序并跳过merge()方法.(<<算法 第四版>>p175页,不懂)
- 不将元素复制到辅助数组: 要做到这一点需要调用两种排序方法, 一种将数据从输入数组排序到辅助数组, 一种将数据从辅助数组排序到输入数组.(<<算法 第四版>>p175页,不懂)
自底向上的归并排序
归并排序的另一种方法是: 两两归并子数组.
1. 轨迹图
注意:sz=1中, 奇数位置的总比偶数位置的短!
2. 归并结果图:
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部分>>