排序算法三——归并排序
归并排序
排序原理
将数组看做左右两半,使左半部分和右半部分成为有序的,那么通过比较左右两半数组的元素再进行一次排序就可以保证数组整体上有序。
排序示意图:
代码实现
public static void process(int[] arr,int L,int R){
if(L == R){
return;
}
//取中点,这里是防止数组特别大时溢出
int mid = L + ((R - L) >> 1);
process(arr,L,mid);
process(arr,mid+1,R);
merge(arr,L,mid,R);
}
public static void merge(int[] arr,int L,int mid,int R){
int[] help = new int[R-L+1];
int i = 0;//用于记录help数组下标移动
int p1 = L;//记录被切分的数组arr左半部分下标移动
int p2 = mid+1;//记录被切分的数组arr右半部分下标移动
while(p1 <= mid && p2 <=R){
//先对左右两半部分进行互相比较 相等的情况优先取左边元素,可以保证排序的稳定性
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while(p2 <=R){
help[i++] = arr[p2++];
}
while(p1 <= mid){
help[i++] = arr[p1++];
}
for (int j = 0; j < help.length; j++) {
arr[L+j] = help[j];
}
}
时间复杂度分析
这里用到了递归,计算的时候可以用master公式。
先简单说一下什么是master公式
T(N)=aT(N/b)+O(Nd) :
N:递归行为的规模|样本数量
T(N):递归的时间复杂度
N/b:递归后子过程的规模
a:子过程调用次数
aT(N/b):所有子过程的时间复杂度
除去递归之外的时间复杂度为O(N^d)
复杂度求解:
(1).logba<d —> O( Nd )
(2).logba>d —> O( Nlogba )
(3).logba==d —>O( Nd * log(N) )
在归并排序中, 递归子过程规模为 N/2 , 递归子过程次数为 2,除去递归以外的其他步骤(例如计算中点位置这一步骤)时间复杂度为O(N)
那么此时a=2 b=2 d=1 此时log22==1 时间复杂度为O(N * log(N))
额外空间复杂度分析
在merge的过程中,需要开辟一个N/2的空间用来存放数据,故额外空间复杂度为O(N)
稳定性分析
在merge的过程中,左右两个数组中指针指向的元素(代码实现中p1,p2两个变量)相等时,先拷贝左侧数组元素到辅助数组中,即可以保证稳定性