归并排序
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并
归并排序(分治法):
- 分:将元素序列不断细分,分到最小子序列段(递归处理)
- 治:合并、排序,递归分子序列段,回溯返回时合并排序

递归的思想解决:分方法一共执行了7次

归并排序具体步骤
排序[8, 4, 5, 7, 1, 3, 6, 2]数组:
-
递归分割数组,逻辑上形成8个子序列段(通过left、right指针分割,当left、right都指向同一个数组元素,逻辑上这就是一个独立的子序列段)

-
准备一个和原数组一样容量的初始数组temp,用于存放临时数据,有一个mid指针
int mid = (left + right) / 2;指向子序列段的中间

-
治:回溯返回到原来的方法,开始合并排序,根据判断
left < right,第一轮是两个元素比较:[0,1],[2,3],[4,5],[6,7],比较值时,该子序列分为两边:[left,mid],[mid+1,right],即[0]-[1],比较arr[left]与arr[mid+1]的值,较小的放入temp[0],另一边放入temp[1],然后temp数组值拷贝到arr


-
后续都是这样运行,直到最后排序
[4,5,7,8] [1, 2, 3, 6]


Java实现归并排序
根据上面的步骤,使用递归+分治实现归并排序
package com.company.sort;
import java.util.Arrays;
/**
* 归并排序算法
*/
public class MergetSort {
private static int num = 0;
public static void main(String[] args) {
int[] arr = {8,4,5,7,1,3,6,2};
int length = arr.length;
int[] temp = new int[length];
mergetSort(arr,0,length - 1,temp);
System.out.println("归并后的数组:"+ Arrays.toString(arr));
System.out.println("归并排序merget执行了:"+num + "次");
}
/**
* @param arr 要排序的原始数组
* @param left 左边有序序列的初始索引
* @param right 右边有序序列的最右边索引
* @param temp 中转的数组
* 分+合
*/
public static void mergetSort(int[] arr,int left,int right,int[] temp){
if (left < right){
int mid = (left + right) / 2;
//向左递归进行分解
mergetSort(arr,left,mid,temp);
//向右递归进行分解
mergetSort(arr,mid+1,right,temp);
merget(arr,left,mid,right,temp);
}
}
/**
* @param arr 要排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边有序序列的最右边索引
* @param temp 中转的数组
* 合并方法
*/
public static void merget(int[] arr,int left,int mid,int right,int[] temp){
num++;
//左边序列的初始索引
int i = left;
//右边有序序列的初始索引
int j = mid + 1;
//中转数组的索引
int t = 0;
//1.先把左右两边(有序)的数据按规则填充到temp数组
//直到左右两边的有序序列有一边处理完毕为止
while (i <= mid && j <= right){
//如果左边元素小于右边,把左边元素拷贝temp,指针后移
if (arr[i] <= arr[j]){
temp[t] = arr[i];
t++;
i++;
}else {
//反之拷贝右边
temp[t] = arr[j];
t++;
j++;
}
}
//2. 把有剩余数据的一边的数据依次全部填充到temp
while (i <= mid){
//说明左边有序序列还有剩余,全部填充到temp
temp[t] = arr[i];
t++;
i++;
}
while (j <= right){
//说明右边有序序列还有剩余,全部填充到temp
temp[t] = arr[j];
t++;
j++;
}
//3. 将temp数组的元素拷贝到原数组arr
t = 0;
int tempLeft = left;
while (tempLeft <= right){
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
}

归并排序速度测试
归并排序时间复杂度:O(nlog2n)
空间复杂度:O(N),需要一个与原数组相同长度的数组temp辅助排序
时间复杂度是一回事,速度又是一回事,我们可以设置80000个0~80000内的数据排序
public static void main(String[] args) {
//int[] arr = {8,4,5,7,1,3,6,2};
int[] arr = new int[80000];
for(int i = 0 ;i <arr.length;i++){
//随机生成80000内的整数
arr[i] = (int) (Math.random()*80000);
}
int length = arr.length;
int[] temp = new int[length];
Date dataBefore = new Date();
mergetSort(arr,0,length - 1,temp);
Date dateAfter = new Date();
System.out.println("消耗了:"+(dateAfter.getTime()-dataBefore.getTime())+"ms");
}

归并排序确实是非常快
同为平均时间复杂度O(nlog2n)
希尔排序也差不多:

快速排序在小数据量上较差一些,但如果在千万级那种数据量大的情况下更强:

本文深入讲解归并排序算法,一种基于分治法的有效排序方法。文章详细阐述了归并排序的具体步骤,包括如何通过递归将序列分割为子序列,以及如何合并这些子序列使其有序。同时,提供了Java实现代码,并进行了归并排序的时间和空间复杂度分析。

1810

被折叠的 条评论
为什么被折叠?



