归并排序

缘起

邓俊辉 数据结构和算法 第三章 例子.

分析

归并排序其实就是分治+二路归并. 归并的过程可以理解为两个非降序列的合并.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "stdafx.h"
#include <iostream>
#include <algorithm>

#define LOCAL
#define N 10

using namespace std;

// 二路归并
void merge(int *a, int lo, int mi, int hi)
{
int n = mi - lo; // a[lo,mi) 数组的个数
int m = hi - mi; // a[mi,hi) 数组的个数
int *tmp = new int[n]; // 注意, c/c++ 不能写 int tmp[n] 这种变长数组 只能写 new int[n] 这种动态开辟的数组空间
memcpy(tmp,a+lo, n*sizeof(int)); // 将a+lo 开始的n个数组元素拷贝到tmp中去, 则归并的源头有2个, 一个是tmp 一个是a+mi 前者 n个元素, 后者m个元素
int i = 0, j =0, k =0; // i,j,k 分别对应a,tmp,a+mi 上的移动指针.
while(j<n && k<m) tmp[j]<a[mi+k]?a[lo+i++] = tmp[j++]:a[lo+i++] = a[mi+k++]; // 如果2个都没有结束
// 下面一定是tmp 或者 a+mi 两个数组中的一个完结了
while(j<n) a[lo+i++] = tmp[j++]; // 继续完结tmp(如果tmp 尚未完结的话)
while (k<m) a[lo+i++] = a[mi+k++]; // 继续完结a+mi(如果a+mi尚未完结的话)
delete []tmp; // 手动释放当时开辟的数组空间
}

// a是数组, n是数组元素的个数 归并排序 [lo, hi)
void sort(int *a, int lo, int hi)
{
if (lo+1==hi) return; // 如果考虑的区间只有一个元素 [lo, hi) 则直接返回, 不做处理了
int mi = (lo+hi)>>1;
sort(a,lo,mi); // 分治 [lo, mi)
sort(a, mi, hi); // 分治 [mi, hi)
merge(a, lo, mi, hi); // 二路归并
}

int main()
{
#ifdef LOCAL
freopen("d:\\data.in", "r", stdin);
#endif

int a[N];
for (int i = 0; i < N; i++) scanf("%d", &a[i]);
sort(a, 0, N);
for (int i = 0; i < N; i++) cout<< a[i]<< endl;
return 0;
}

归并排序是稳定的, O(nlogn)复杂度、O(n)空间复杂度的算法.