归并排序核心思想:
将一个数组一分二,然后将两个数组分别排序,最后将两个有序数据合并,就完成了归并排序。
归并排序使用的是分治思想,分而治之,将一个大问题分解为n个小问题,小问题解决了,大问题也就解决了。
归并排序一般使用递归来实现,要写出递归代码的关键,写出递推公式,找到终止条件。
归并排序的递推公式:
merge_sort(p…r) = merge(merge_sort(p…q),merge_sort(q+1….r))
终止条件:q>= r
递推公式注释:
merge_sort(p…r)表示我们要排序的数组的下标在p…r区间,将这个大的数组的排序问题转化为了两个子数组排序的问题。即排序p到q区间和q+1到r区间,q=(p+r)/2,当这两个数组排序完成后,使用merge合并函数进行数组的合并,整个数组即可为有序。
再来说说merge合并函数
还是先画个图
代码实现
public class MergerSort< T > implements SortInf< T > { @Override public void sort ( T [] data) { if ( null == data || data. length <= 1 ) { return; } if (!(data[ 0 ] instanceof Comparable)) { throw new IllegalArgumentException( "data not implement compator interface" ) ; } // 1, 归并排序操作 recurstion(data , 0 , data. length - 1 ) ; } private void recurstion ( T [] data , int start , int end) { // 递归的终止条件 if (start >= end) { return; } // 1, 找到中间的索引位置 int midIndex = (start + end) / 2 ; // 进行左半边数据递归操作 recurstion(data , start , midIndex) ; // 进行右半边数据递归操作 recurstion(data , midIndex + 1 , end) ; // 进行 mergesort 的合并操作 merget(data , start , midIndex , midIndex + 1 , end) ; } /** * 进行数据的合并操作 * * @param data 原始数据 * @param leftstart 左开始索引 * @param leftEnd 左结束索引 * @param rightStart 右边开始索引 * @param end 右边结束索引 */ private void merget ( T [] data , int leftstart , int leftEnd , int rightStart , int end) { int dataLength = end - leftstart + 1 ; // 声明一个目标的数组大小 T [] target = ( T []) Array.newInstance (data[ 0 ].getClass() , dataLength) ; int tarIndex = 0 ; int leftIndex = leftstart ; int rightIndex = rightStart ; while (leftIndex <= leftEnd && rightIndex <= end) { // if i < j ? -1 == 0 > 1 if (((Comparable) data[leftIndex]).compareTo(data[rightIndex]) > 0 ) { target[tarIndex++] = data[rightIndex++] ; } else { target[tarIndex++] = data[leftIndex++] ; } } // 将剩余的值拷贝到目标数组中 if (leftIndex <= leftEnd) { for ( int i = leftIndex ; i <= leftEnd ; i++) { target[tarIndex++] = data[i] ; } } else if (rightIndex <= end) { for ( int i = rightIndex ; i <= end ; i++) { target[tarIndex++] = data[i] ; } } // 将数据拷贝回原数组中 for ( int i = leftstart ; i <= end ; i++) { data[i] = target[i - leftstart] ; } System. out .println(Arrays.toString (data)) ; } }
如何分析归并排序的时间复杂度
在递归的场景中,一个问题a可以分解为几个子问题b和c,将问题b和问题c求解后,再将问题b和问题c的结果合并,就可以得到问题a的求解
定义求解的问题,a的时间为t(a),求解b、c的时间时间分为别t(b)和t(c),合并t(b)和t(c)的时间为k,代入递推公式就各要得到:
t(a)=t(b)+t(c)+k
在归并排序中,每次都将数据一分为二,可以表示为n/2,merge合并函数每次都需要将n个数据合并,可以表示为n,递推关系式可以改写为为:
t(n)=2*t(n/2)+n
t(1)=C,,当求解的数组大小为1时,只需要常量级的时间,所以表示为C
可获得结果
再次计算
结果
再次计算
结果
总结规律
可得到
可得到 k 的取值
t(1)=C
将 k 代入
使用 O 表示法就是
归并排序的总结:
算法名称
最好情况时间复杂度
最好情况的原始数据
最坏情况时间复杂度
最坏情况的原始数据
平均情况时间复杂度
是否基于比较
空间复杂度
是否稳定排序算法
归并排序
O(nlogn)
与原始数据的有序度无关
O(nlogn)
与原始数据有序度无关
O(nlogn)
是
O(n)
非原地排序算法
移定的原地排序算法。