归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略
可以看出这种结构像一个完全二叉树,递归深度为log2n
从上往下的归并排序:它与"从下往上"在排序上是反方向的。它基本包括3步:
① 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2;
② 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。
③ 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]。
public class HelloWorld {
public static void MergeSort(int[] arr,int low,int high)
{
if(low<high)//能再分,就继续将数组分割
{
int mid=(low+high)/2;
//low-mid有序,mid+1-high有序
MergeSort(arr,low,mid);
MergeSort(arr,mid+1,high);
//将arr low-mid,mid+1-high合并成有序
Sort(arr,low,mid,high);
}
}
public static void Sort(int[] arr,int low,int mid,int high)
{
int i=low;//指向low-mid数组的起点
int j=mid+1;//指向mid+1-high数组的起点
int[] temp=new int[high-low+1];//为了不破坏arr数组,将数据先放入temp中
int k=0;//k是此次要存入temp的索引
//将数据从小到大存入到temp数组中
for(;i<=mid&&j<=high;k++)
{
if(arr[i]<=arr[j]) //注意这里是<=,确保了稳定性
{
temp[k]=arr[i];
i++;
}
else{
temp[k]=arr[j];
j++;
}
}
//可能存在 第一个数组的数均大于第二个数组,或者第一个数组数据有剩余
for(;i<=mid;i++)
{
temp[k]=arr[i];
k++;
}
//同理,第二个数组
for(;j<=high;j++)
{
temp[k]=arr[j];
k++;
}
//将temp数据放入arr中相应位置
for(int m=0;m<temp.length;m++)
{
arr[low+m]=temp[m];
}
}
public static void main(String []args) {
int[] arr=new int[]{10,8,100,101};
MergeSort(arr,0,arr.length-1);
for(int m=0;m<arr.length;m++)
{
System.out.println(arr[m]);
}
}
}
一、时间复杂度、空间复杂度
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。归并排序的最好、最坏和平均的时间复杂度均为O(nlogn),空间复杂度为O(n)。归并排序比较占用内存(相对于快排的空间复杂度O(logn)以及堆排序的空间复杂度O(1),其中三者时间复杂度相同),当n比较大的时候,归并排序往往会出内存溢出错误。但却是一种效率高且稳定的算法。
二、稳定性
在分解的子列中,有1个或2个元素时,1个元素不会交换,2个元素如果大小相等也不会交换。在序列合并的过程中,如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,所以,归并排序也是稳定的。