归并排序通过递归不断地将待排序数据集合平均分成左右两个区间分别排序,然后将左右两个完成排序的区间进行归并处理,最终使得整个数据集合完成排序。
代码template
class mergeSort {
public:
void sort(std::vector& data)
{
// 归并排序依赖于辅助空间,非原地算法
// 针对辅助空间,需要一次性分配好所有辅助空间,避免临时内存分配
buffer_.resize(data.size());
sort(data, 0, static_cast(data.size()) - 1);
}
private:
void sort(std::vector& data, std::ptrdiff_t left, std::ptrdiff_t right)
{
// 当子问题最多只存在一个元素时,无需继续处理,直接返回。
if (left >= right) {
return;
}
std::ptrdiff_t mid = left + (right - left) / 2;
sort(data, left, mid); // 先对左区间[left, mid]内元素排序
sort(data, mid + 1, right); // 再对右区间[mid + 1, right]内元素排序
// 如果左区间的最大值不大于右区间的最小值,整个区间内数据已经有序,无需进行归并处理
if (data[mid] <= data[mid + 1]) {
return;
}
// 对左区间和右区间的数据进行归并处理,归并处理完后整个区间数据完成排序
merge(data, left, mid, right);
}
void merge(std::vector& data, std::ptrdiff_t left, std::ptrdiff_t mid, std::ptrdiff_t right)
{
// 拷贝数据到辅助空间
std::copy(data.begin() + left, data.begin() + right + 1, buffer_.begin() + left);
std::ptrdiff_t i = left;
std::ptrdiff_t j = mid + 1;
for (std::ptrdiff_t index = left; index <= right; ++index) {
if (i > mid) {
data[index] = buffer_[j++]; // 左边区间数据已遍历完,只需要处理右边数据
} else if (j > right) {
data[index] = buffer_[i++]; // 右边区间数据已遍历完,只需要处理左边数据
} else if (buffer_[i] > buffer_[j]) {
data[index] = buffer_[j++]; // 如果左边数据大于右边的数据,处理右边数据
} else {
data[index] = buffer_[i++]; // 如果左边数据不大于右边的数据,处理左边数据
}
}
}
private:
std::vector buffer_;
};
归并排序代码中有两点需特别注意:
1、辅助空间预先完成分配。由于归并算法是非原地算法,需要借助辅助空间对数据进行排序。为了避免在归并处理过程中临时多次分配和释放内存导致的额外开销,可以预先一次分配好内存,在使用时赋值即可。
2、剪枝处理。归并处理是将有序的左区间和有序的右区间进行合并处理,如果有序左区间的最大值不大于有序右区间的最小值,此时整个区间已经有序,无需进行归并处理。如果待排序的数据是有序的,通过剪枝处理后归并算法时间复杂度为O(n)
。
空间复杂度:O(n)
,归并排序依赖辅助空间,空间大小与待排序元素个数成正比。
时间复杂度:O(nlogn)
,归并排序通过递归不断地将待排序区间一分为二,递归的深度为O(logn)
,每层的处理为O(n)
,排序整体下来后为O(nlogn)
。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)