归并排序
把数组平均分成两半,若两个区间有序后,则进行归并。使两个区间有序和快排同样的方法一样,递归到最后只有一个值或者没有时即有序。但在归并时需要开辟一段空间把合并的数据存起来,再放回原数组。
适合外部排序
可以实现外部排序,把两个有序的文件,依次读取出来归并到一个新的文件里。
注意
归并时选择选择两个数组头部小的一个时,要有等于号,否则两个头部相等时会发生死循环。
优化
- 小区间优化
小区间优化在快排里有讲
时间复杂度
O(n*lgn)
代码实现
void MergeSort(int *a, int left, int right)
{
if (left >= right)
return;
int mid = left + ((right - left) >> 1);
MergeSort(a, left, mid);
MergeSort(a, mid+1, right);
//归并
Merge(a, left, mid, right);
}
void Merge(int *a, int begin ,int mid,int end)
{
int *tmp = new int[end - begin + 1];
int begin1 = begin;
int begin2 = mid+1;
int end1 = mid;
int end2 = end;
int index = 0;
while (begin1 <= end1&&begin2 <= end2)
{
while (begin1<=end1&&a[begin1] <= a[begin2])
{
tmp[index++] = a[begin1++];
}
while (begin2<=end2&&a[begin1]>=a[begin2])
{
tmp[index++] = a[begin2++];
}
}
while (begin2 <= end2&&begin1 > end1)
{
tmp[index++] = a[begin2++];
}
while (begin1 <= end1&&begin2 > end2)
{
tmp[index++] = a[begin1++];
}
for (int i = begin, j = 0; i <= end; i++, j++)
{
a[i] = tmp[j];
}
delete[] tmp;
}
非递归
循环的非递归,先一一归并,然后再两两归并,直到最后一半与另一半归并。
代码实现
//[left,right]
void MergeSortNR(int *a, int left, int right)
{
int begin = 0;
int end = 0;
int mid = 0;
//gap是两个要和并分组其中一个的步长。
for (int gap = 1;gap<=(right-left+1);gap*=2)
{
begin = left;
while (1)
{
mid = begin + gap - 1;
end = begin + 2 * gap - 1;
//mid>right剩下的不足一个组,肯定已经有序,因为gap是从1增长的。
if (mid>right)
{
break;
}
//中间值没有超,右边界超了,[begin,mid] [mid,right]
if (end > right)
{
end = right;
}
MergeNR(a, begin, mid, mid + 1, end);
begin = end + 1;
}
}
}
void MergeNR(int *a, int begin1, int end1,int begin2, int end2)
{
int *tmp = new int[end2 - begin1 + 1];
int left = begin1;
int right = end2;
int index = 0;
while (begin1 <= end1&&begin2 <= end2)
{
while (begin1 <= end1&&a[begin1] <= a[begin2])
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2&&a[begin1] > a[begin2])
{
tmp[index++] = a[begin2++];
}
}
while (begin2 <= end2&&begin1 > end1)
{
tmp[index++] = a[begin2++];
}
while (begin1 <= end1&&begin2 > end2)
{
tmp[index++] = a[begin1++];
}
for (int i = left, j = 0; i <= right; i++, j++)
{
a[i] = tmp[j];
}
delete[] tmp;
}
- 空复复杂度高,需要开辟同等大小的空间
- 但归并排序可以进行外部排序