《算法(第4版)》笔记
归并
归并排序基于“归并”这个简单的操作,即将两个有序的数组归并成一个更大的有序数组。
要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。
// c++
template<class T>
void merge(T& a, int lo, int mid, int hi, T& aux)
{
// 将a[lo .. mid]和a[mid+1 .. hi]归并
int i = lo;
int j = mid + 1;
for (int k=lo; k<=hi; ++k)
aux[k] = a[k];
for (int k=lo; k<=hi; ++k)
{
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (aux[j] < aux[i])
a[k] = aux[j++];
else
a[k] = aux[i++];
}
}
# python
def merge(a, lo, mid, hi, aux):
# 将a[lo .. mid]和a[mid+1 .. hi]归并
i = lo
j = mid + 1
for k in range(lo, hi + 1):
aux[k] = a[k]
for k in range(lo, hi + 1):
if i > mid:
a[k] = aux[j]
j += 1
elif j > hi:
a[k] = aux[i]
i += 1
elif aux[j] < aux[i]:
a[k] = aux[j]
j += 1
else:
a[k] = aux[i]
i += 1
自顶向下的归并排序(递归)
// c++
template<class T>
void merge_sort(T& a, int lo, int hi, T& aux)
{
if (hi <= lo)
return;
int mid = lo + (hi - lo) / 2;
merge_sort(a, lo, mid, aux); // 将左半边排序
merge_sort(a, mid + 1, hi, aux); // 将右半边排序
merge(a, lo, mid, hi, aux); // 归并结果
}
// 将容器a按升序排列
template<class T>
void merge_sort(T& a)
{
T aux;
aux.resize(a.size());
merge_sort(a, 0, a.size() - 1, aux);
}
# python
def merge_sort_p(a, lo, hi, aux):
if hi <= lo:
return
mid = lo + (hi - lo) // 2
merge_sort_p(a, lo, mid, aux)
merge_sort_p(a, mid + 1, hi, aux)
merge(a, lo, mid, hi, aux)
# 将列表a按升序排列
def merge_sort(a):
aux = [None] * len(a)
merge_sort_p(a, 0, len(a) - 1, aux)
归并排序所需的时间和NlogN成正比。
可以用归并排序处理数百万甚至更大规模的数组,这是插入排序或者选择排序做不到的。归并排序的主要缺点是辅助数组所使用的额外空间和N的大小成正比。
通过一些细致的思考我们还能够大幅度缩短归并排序的运行时间,比如对小规模子数组使用插入排序。使用插入排序处理小规模的子数组(比如长度小于15)一般可以将归并排序的运行时间缩短10%~15%。
自底向上的归并排序
实现归并排序的另一种方法是先归并那些微型数组,然后再成对归并得到的子数组,如此这般,直到我们将整个数组归并在一起。这种实现方法比标准递归方法所需要的代码量更少。首先我们进行两两归并(把每个元素想象成一个大小为1的数组),然后是四四归并(将两个大小为2的数组归并成一个有4个元素的数组),然后是八八归并,一直下去。
// c++
// 将容器a按升序排列
template<class T>
void mergeBU_sort(T& a)
{
int n = a.size();
T aux;
aux.resize(n);
for (int sz = 1; sz < n; sz += sz)
for (int lo = 0; lo < n - sz; lo += sz + sz)
merge(a, lo, lo + sz - 1, std::min(lo + sz + sz - 1, n - 1), aux);
}
# python
# 将列表a按升序排列
def mergeBU_sort(a)
n = len(a)
aux = [None] * n
sz = 1
while sz < n:
for lo in range(0, n - sz, sz + sz):
merge(a, lo, lo + sz -1, min(lo + sz + sz - 1, n - 1), aux)
sz += sz