前言
一、归并排序是什么?
归并排序的思路:
不断对数组进行二分,直到分组后序列的长度小于等于1,然后将两个子序列合并,得到一个有序的序列。
2-路归并实现的步骤:
1.先将数组分段,对每一段元素进行排序
2.创建一个新的数组,对两组排好序的序列进行合并
3.把元素从新的数组搬回原数组中
二、归并排序的实现
1.归并排序
代码如下:
public class mergeSort {
public static void mergeSort(long[] array){
mergeSortInternal(array,0,array.length);
//数组Array为左闭右开区间
}
public static void mergeSortInternal(long[] array,int lowIndex,int highIndex){
int size=highIndex-lowIndex;
if(size<=1){
return;
}
//区间都是左闭右开区间,middleIndex不包括在左边的数组中,包含在右边的数组中
int middleIndex=(lowIndex+highIndex)/2;
mergeSortInternal(array,lowIndex,middleIndex);
mergeSortInternal(array,middleIndex,highIndex);
合并两个有序区间(array,lowIndex,middleIndex,highIndex);
}
2.合并两个有序序列
代码如下(示例):
private static void 合并两个有序区间(long[] array,int lowIndex,int middleIndex,int highIndex){
int size=highIndex-lowIndex;
long[] extraArray=new long[size];
int leftIndex=lowIndex;
int rightIndex=middleIndex;
int extraIndex=0;
//两个序列都有元素时
while(leftIndex<middleIndex&&rightIndex<highIndex){
if(array[leftIndex]<=array[rightIndex]){
extraArray[extraIndex]=array[rightIndex];
leftIndex++;
extraIndex++;
}else{
extraArray[extraIndex]=array[rightIndex];
extraIndex++;
rightIndex++;
}
}
//开始有一个序列没有元素了
if(leftIndex<middleIndex){
while(leftIndex<middleIndex){
extraArray[extraIndex++]=array[leftIndex++];
}
}else{
while(rightIndex<highIndex){
extraArray[extraIndex++]=array[rightIndex++];
}
}
3.将合并后的序列元素搬回去
for(int i=0;i<size;i++){
array[i+lowIndex]=extraArray[i];
}
三、性能分析
时间复杂度:最好、最坏、平均都为O(n*log(n))
空间复杂度:都是O(log(n))
稳定性:稳定
四、海量数据的排序问题
前提:内存只有1G,需要排序的数据有100G
海量数据的特点就是:内存中无法把所有的数据全部放进去进行排序,所以需要外部排序(借助磁盘等外部存储进行的排序),而归并排序就是最常用的外部排序,简称多路排序。
1.把数据切割成n份(200份,每份512M);
2.分别对512M排序,因为内存已经放得下,任意的排序都可以,并且可以将任务分配给对个机器共同参与排序;
3.进行200路归并,同时对200份数据进行排序的过程,将每份文件中 最小的数字放到内存中(实际上也可以不止放入一个),将其中最小的数选出来,尾插到最后有序的文件中。
总结
1.当数据的元素个数比较少的时候,可以采用其他的排序方法(比如当元素个数小于16个时,建议使用插排),因为归并排序对于比较小的区间总是要不断地递归调用,开销比较大,不划算,因此不建议使用。
2.设计归并排序的时候,求数组的长度时,要注意每一小段区间下标的包含不包含问题,示例代码中采用的是左闭右开的区间。