1 排序原理
归并排序是分治法的一个典型应用,简单来说,归并排序分为两部分,第一部分是拆,就是将数组的长度一层一层往下拆,第二部分就是将拆好的一个一个相邻的数据进行合并,在这一步里面我们进行排序。整体思想其实非常简单
2 时间复杂度
O(logn*n)
正如其他的高级排序算法一样,归并排序其实也就是减少我们进行排序的重复操作。拆分的时候,采用二分的方式,所以有logn层,而每层比较的次数就是n喽,所以其时间复杂度为O(logn*n),
其实仔细想一想,所有的排序算法其本质差不多啦,就是较少重复操作,而如何减少重复操作呢,就是让我之前的操作对后序操作产生影响,这一点,归并排序比较明显,在其合并的时候,我合并的一个一个相邻的子数组,其实其内部是有序的,这也就是为什么归并排序效率高的原因了
额外说一句,外排序的基础也是归并排序
3 算法实现
/**
* 归并排序--分为两部分 拆:递归拆 合:进行排序
*
* @author xld
*
*/
public class MergeSort {
public static void sort(int[] arr) {
int n = arr.length;
sort(arr, 0, n - 1);
}
/**
* 注意 这里的数组的取值范围为[l,r],也就是说,左右下标都可以取到
*
* @param arr
* 数组
* @param l
* 数组左下标
* @param r
* 数组右下标
*/
private static void sort(int[] arr, int l, int r) {
if (l >= r) {
return;
}
int mid = (l + r) / 2;
sort(arr, l, mid);
sort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
/**
* 合:真正将数据进行排序 将两个相邻的数组进行合并 是相邻的哦
*
* @param arr
* @param l
* @param mid
* @param r
*/
private static void merge(int[] arr, int l, int mid, int r) {
int[] aux = new int[r - l + 1];
int auxIndex = 0;
for (int i = l; i <= r; i++) {
aux[auxIndex++] = arr[i];
}
int leftIndex = l;
int rightIndex = mid + 1;
for (int i = l; i <= r; i++) {
if (leftIndex > mid) {
arr[i] = aux[rightIndex-l];
rightIndex++;
} else if (rightIndex > r) {
arr[i] = aux[leftIndex-l];
leftIndex++;
} else if (aux[leftIndex-l] <= aux[rightIndex-l]) {
arr[i] = aux[leftIndex-l];
leftIndex++;
} else {
arr[i] = aux[rightIndex-l];
rightIndex++;
}
}
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
int[] arr = {10,9,8,7,6,5,4,3,2,1};
MergeSort.sort(arr);
for( int i = 0 ; i < arr.length ; i ++ ){
System.out.print(arr[i]);
System.out.print(' ');
}
System.out.println();
}
}
4 优化
1 当数据量比较小的时候,可以采用插入排序进行优化,这是因为小的数据量,进行多次递归的消耗就犯不上了
2 当数据是基本有序的情况下,在合并的时候可以进行判断,如果前一个数组的最后一个元素比后一个数组的第一个元素还要小,那么就可以直接合并,无需比较了