作为数据结构的课程笔记,以便查阅。如有出错的地方,还请多多指正!
归并
- 归并:将两个或两个以上的有序表组合成一个新的有序表
- 初始: j = m + 1 , k = i j=m+1, k=i j=m+1,k=i
- 当 i < = m & & j < = n i<=m\ \&\&\ j<=n i<=m && j<=n 时,若 S R [ i ] . k e y ≤ S R [ j ] . k e y SR[i].key\leq SR[j].key SR[i].key≤SR[j].key, T R [ k ] = S R [ i + + ] TR[k] = SR[i++] TR[k]=SR[i++];否则 T R [ k ] = S R [ j + + ] TR[k] = SR[j++] TR[k]=SR[j++]; k + + k++ k++
- 如有剩余记录,复制
void Merge(Rec_t* src, Rec_t* tmp, int i, int m, int n)
{
int j = m + 1;
int k = i;
while (i <= m && j <= n)
{
if (src[i].key <= src[j].key)
{
tmp[k++] = src[i++];
}
else {
tmp[k++] = src[j++];
}
}
while (i <= m)
{
tmp[k++] = src[i++];
}
while (j <= m)
{
tmp[k++] = src[j++];
}
}
思路
- 通过归并两个或两个以上的记录有序子序列,逐步增加记录有序序列的长度
- 设初始序列含有 n n n 个记录,则可看成 n n n 个有序的子序列,每个子序列长度为 1
- 两两合并,得到 ⌈ n / 2 ⌉ \lceil n/2\rceil ⌈n/2⌉ 个长度为 2 或 1 的有序子序列
- 重复上一步,直至得到一个长度为
n
n
n 的有序序列为止
递归实现
- 2-路归并排序的递归写法非常简单, 只需要反复将当前区间分为两半, 对两个子区间分别递归进行归并排序, 然后将两个已经有序的子区间合并为有序序列即可
- 将 S R SR SR 平分成两个无序子序列 S R [ s . . m ] SR[s..m] SR[s..m] 和 S R [ m + 1.. t ] SR[m+1..t] SR[m+1..t]
- 将 S R [ s . . m ] SR[s..m] SR[s..m] 归并为有序 T R 2 [ s . . m ] TR2[s..m] TR2[s..m]
- 将 S R [ m + 1.. t ] SR[m+1..t] SR[m+1..t] 归并为有序 T R 2 [ m + 1.. t ] TR2[m+1..t] TR2[m+1..t]
- 调用
Merge()
将有序 T R 2 [ s . . m ] TR2[s..m] TR2[s..m] 和 T R 2 [ m + 1.. t ] TR2[m+1..t] TR2[m+1..t] 合并为有序
void M_sort_recursive(Rec_t* src, Rec_t* dst, int s, int t)
{
static Rec_t tmp[MAX_SIZE + 1];
if (s == t)
{
dst[s] = src[s];
}
else {
int mid = (s + t) / 2;
M_sort_recursive(src, tmp, s, mid);
M_sort_recursive(src, tmp, mid + 1, t);
Merge(tmp, dst, s, mid, t);
}
}
//2-路归并排序 递归实现
void Merge_sort_recursive(SqList_t* list)
{
M_sort_recursive(list->rec, list->rec, 1, list->len);
}
非递归实现
- (1) 先将待排序列中有序子列的长度看作 1
- (2) 对待排序列中所有有序子列进行两两归并,此时待排序列中有序子列的长度翻倍
- (3) 重复上一步,直至有序子列长度大于等于待排序列的长度
// n 为元素个数,len 为当前有序子列的长度
// 该函数对所有有序子列进行归并
void Merge_pass(Rec_t* src, Rec_t* tmp, int n, int len)
{
int i;
for (i = 1; i + 2 * len <= n; i += len * 2)
{
Merge(src, tmp, i, i + len - 1, i + 2 * len - 1);
}
if (i + len <= n) // 归并最后两个有序子列
{
Merge(src, tmp, i, i + len - 1, n);
}
else { // 归并最后一个有序子列
while (i <= n)
{
tmp[i] = src[i];
++i;
}
}
}
void Merge_sort(SqList_t* list)
{
Rec_t* tmp = (Rec_t*)malloc((list->len + 1) * sizeof(Rec_t));
int len = 1; // 当前有序子列的长度
while (len < list->len)
{
Merge_pass(list->rec, tmp, list->len, len);
len *= 2;
Merge_pass(tmp, list->rec, list->len, len);
len *= 2;
}
free(tmp);
}
算法评价
T ( n ) T(n) T(n)
T ( n ) = 2 T ( n 2 ) + c n ∴ T ( n ) = O ( n log n ) T(n)=2T(\frac{n}{2}) + cn \\\therefore T(n)=O(n\log n) T(n)=2T(2n)+cn∴T(n)=O(nlogn)
S ( n ) S(n) S(n)
S ( n ) = O ( n ) S(n)=O(n) S(n)=O(n)
是否稳定
- 稳定
总结
- 因为要在空间之间来回复制数据,效率比较低,因此一般很少利用 2-路归并排序进行内部排序,特别是递归形式。它一般用在外部排序中