归并排序
归并排序的核心思想:
将无序数组分成两份,分别对左边和右边排序;
将排好序的左边和排好序的右边合并在一起;
public class _08_Merge1 {
public static void mergeSort(int[] arr) {
mergeSort(arr, 0, arr.length - 1);
}
private static void mergeSort(int[] arr, int low, int high) {
if (low == high) return;
int mid = (low + high) >> 1;
//2. sort left
mergeSort(arr, low, mid);
//3. sort right
mergeSort(arr, mid + 1, high);
//4. merge
merge(arr, low, mid, high);
}
private static int[] copyList(int[] arr, int start, int end) {
int[] result = new int[end - start + 1];
for (int i = start; i <= end; i++) {
result[i - start] = arr[i];
}
return result;
}
private static void merge(int[] arr, int low, int mid, int high) {
int left = 0;
int right = 0;
int[] leftArr = copyList(arr, low, mid);
int leftLen = leftArr.length;
int[] rightArr = copyList(arr, mid + 1, high);
int rightLen = rightArr.length;
for (int i = low; i <= high; i++) {
if (left >= leftLen || right < rightLen && leftArr[left] >= rightArr[right]) {
arr[i] = rightArr[right];
right++;
} else {
//else if(right > high || left <= mid && arr[left] <= arr[right] )
arr[i] = leftArr[left];
left++;
}
}
}
public static void main(String[] args) {
int[] arr = {26, 15, 23, 48, 54, 2, 35, 9, 20, 36, 71, 32, 37, 65};
mergeSort(arr);
System.out.println(Arrays.toString(arr));
}
}
【_08_MergeSort】
稳定性:true 耗时:0.006s(6ms) 比较:26.09万 交换:0
------------------------------------------------------------------
【_03_HeapSort】
稳定性:false 耗时:0.012s(12ms) 比较:51.05万 交换:2.00万
------------------------------------------------------------------
【_05_Insert_BinarySearch】
稳定性:true 耗时:0.261s(261ms) 比较:26.07万 交换:0
------------------------------------------------------------------
【_02_Select】
稳定性:true 耗时:0.393s(393ms) 比较:2.00亿 交换:2.00万
------------------------------------------------------------------
【_04_Insert】
稳定性:true 耗时:0.421s(421ms) 比较:1.01亿 交换:1.01亿
------------------------------------------------------------------
【_04_Insert2】
稳定性:true 耗时:0.555s(555ms) 比较:1.34亿 交换:0
------------------------------------------------------------------
【_01_Bubble】
稳定性:true 耗时:1.287s(1287ms) 比较:2.00亿 交换:1.01亿
------------------------------------------------------------------
【_01_Bubble_3】
稳定性:true 耗时:1.827s(1827ms) 比较:2.00亿 交换:1.01亿
------------------------------------------------------------------
【_01_Bubble_2】
稳定性:true 耗时:1.875s(1875ms) 比较:2.00亿 交换:1.01亿
------------------------------------------------------------------
复杂度分析
T(n) = 2 ∗ T(n/2) + O(n)
T(1) = 1
最好、最坏时间复杂度都 O(nlogn)
常见的递推式与复杂度
merge优化
只将左边备份出来,这样节省了空间;
从左往右添加到arr中,如果左边数组遍历结束了,说明左边的插完了
/**
* 归并排序
*/
@SuppressWarnings("unchecked")
public class MergeSort <T extends Comparable<T>> extends Sort<T> {
private T[] leftArray;
@Override
protected void sort() {
// 准备一段临时的数组空间, 在merge操作中使用
leftArray = (T[])new Comparable[array.length >> 1];
sort(0, array.length);
}
/**
* 对 [begin, end) 范围的数据进行归并排序
*/
private void sort(int begin, int end){
if(end - begin < 2) return; // 至少要2个元素
int mid = (begin + end) >> 1;
sort(begin, mid); // 归并排序左半子序列
sort(mid, end); // 归并排序右半子序列
merge(begin, mid, end); // 合并整个序列
}
/**
* 将 [begin, mid) 和 [mid, end) 范围的序列合并成一个有序序列
*/
private void merge(int begin, int mid, int end){
int li = 0, le = mid - begin; // 左边数组(基于leftArray)
int ri = mid, re = end; // 右边数组(array)
int ai = begin; // array的索引
// 备份左边数组到leftArray
for(int i = li; i < le; i++){
leftArray[i] = array[begin + i];
}
// 如果左边还没有结束
while(li < le){ // li == le 左边结束, 则直接结束归并
if(ri < re && cmp(array[ri], leftArray[li]) < 0){ // cmp改为<=0会失去稳定性
array[ai++] = array[ri++]; // 右边<左边, 拷贝右边数组到array
}else{
array[ai++] = leftArray[li++]; // 左边<=右边, 拷贝左边数组到array
}
}
}
}