归并排序
- 思想
分治 ( 递归 )
对集合一层层拆,拆到元素级开始比较左右两侧,在一层层合并,合并时因为子序列都有序,所以合并时按以此取子集合赋值给主集合位置即可
- 算法图解
定义first && last 简称 F 和 L
first (F0)
last (L9)
mid=(first + last)/2=M4
顺序拆分集合进行递归
- 应用场景
1T 的文件,取 Top K,拆分100M 10个, 分别内存快排,然后采用双路归并排序+多线程处理,每两路取TOP K 后在进一步归并,直到归并成一趟。
- 算法优化(以不大幅度增加代码复杂度为原则)
拆分合并时需要借助临时集合,所以多次申请内存空间,可以预先开辟固定大小集合,当小的时候在增大,调整增大策略
- 平均时间复杂度(稳定)
最差时间复杂度 О(n²)
平均时间复杂度 O(nlogn)
- 空间复杂度
最差空间复杂度 О(n) total, O(1)
- 代码演示(C#版本)
public static void Sort(int[] array, int first, int last)
{
if (first < last) //子表的长度大于1,则进入下面的递归处理
{
int mid = (first + last) / 2; //子表划分的位置
Sort(array, first, mid); //对划分出来的左侧子表进行递归划分
Sort(array, mid + 1, last); //对划分出来的右侧子表进行递归划分
MergeSortCore(array, first, mid, last); //对左右子表进行有序的整合(归并排序的核心部分)
Console.WriteLine(string.Join(",", array));
}
}
private static void MergeSortCore(int[] array, int first, int mid, int last)
{
//Console.WriteLine(string.Join(",", array));
int indexA = first; //左侧子表的起始位置
int indexB = mid + 1; //右侧子表的起始位置
int[] temp = new int[last + 1]; //声明数组(暂存左右子表的所有有序数列):长度等于左右子表的长度之和。
int tempIndex = 0;
while (indexA <= mid && indexB <= last) //进行左右子表的遍历,如果其中有一个子表遍历完,则跳出循环
{
if (array[indexA] <= array[indexB]) //此时左子表的数 <= 右子表的数
{
temp[tempIndex++] = array[indexA++]; //将左子表的数放入暂存数组中,遍历左子表下标++
}
else//此时左子表的数 > 右子表的数
{
temp[tempIndex++] = array[indexB++]; //将右子表的数放入暂存数组中,遍历右子表下标++
}
}
//有一侧子表遍历完后,跳出循环,将另外一侧子表剩下的数一次放入暂存数组中(有序)
while (indexA <= mid)
{
temp[tempIndex++] = array[indexA++];
}
while (indexB <= last)
{
temp[tempIndex++] = array[indexB++];
}
//将暂存数组中有序的数列写入目标数组的制定位置,使进行归并的数组段有序
tempIndex = 0;
for (int i = first; i <= last; i++)
{
array[i] = temp[tempIndex++];
}
}