前面的博文介绍了插入排序
与选择排序
的java代码实现, 这两种排序算法, 适合的场景是小规模数据, 因为时间复杂度为: O(n*n), 大数据量下, 这个复杂度还是有点高, 所以就需要今天该博文给大家介绍的归并排序
算法了.
简单介绍一下归并排序
, 它本质是分治思想的应用(以后的博文会具体介绍分治), 分治概括来说类似递归思想, 不过递归是一种编程手段, 还不是思想层面的东西, 不过经常用递归实现分治思想, 于是就把大规模的数据, 分解为小规模数据量, 各自去处理.
关于归并在排序中的逻辑: 我们不断递归拆分数组的数据, 直到子数组的元素只有一个, 然后每两个子数组合并成一个有序的数组, 还是通过层层递归, 最后合并成原数组长度的有序数组
.
Talk is cheap, show your code! 代码中有详细注释, 太详细了! 没介绍到的地方, 可以在下面评论滴滴我. 一起交流学习.
import org.junit.Test;
/**
* 功能说明:归并排序
* 开发人员:@author MaLi
*/
public class T04_MergeSort {
//排序功能入口
public void sort(int[] arr) {
sort(arr, 0, arr.length - 1);
}
/**
* 递归方法
* 功能: 对arr数组的子区间进行迭代排序
* 解释: 采用分治思想, 使用递归方式实现, 递归公式如下
* sort(arr,start,end) = sort(arr,start,mid)+sort(arr,mid+1,end)
* 递归终止条件: arr子区间的长度==1, 也就是start=end的时候
* @param arr 待排序数组
* @param start 区间左边界
* @param end 区间右边界
*/
public void sort(int[] arr, int start, int end) {
//此处为数组合法性检验
//1, arr为null, 无法排序
//2, arr的元素为1, 只有一个元素, 无需排序
if (arr == null || arr.length <= 1) {
return;
}
//递归代码块
if (start == end) {
//此处是递归终止条件: 即arr子区间的长度==1, 也就是start=end的时候, 递归终止, 动作是: 返回
return;
} else {
int mid = (start + end) / 2;
int[] tmp = new int[end - start + 1]; //使用tmp临时数组的目的: 传递给merge函数, 保存排序子区间, 然后拷贝到arr的指定位置
//此处是递归递推公式的翻译:总体数据在mid出拆分为两个子规模的数据, 分别进行sort排序
// 注意: 理解递归的关键 --- 只要设定好了终止条件写好了递推公式, 人脑不要推断递归过程, 非要理解的话, 记住jvm虚拟机栈中栈帧的工作原理就可以了
sort(arr, start, mid); //leftArr
sort(arr, mid + 1, end);
merge(arr, start, mid, end, tmp);
}
}
/**
* 分治思想的: 治(合)阶段, 在该逻辑实现中目的: 合并排序后的数组
* 具体逻辑: 两个数组合并为有序的一个数组
* 两个数组分别是: leftArr[start,mid] 与rightArr[mid+1,end]
* @param arr 待排序数组
* @param start 区间左边界: leftArr[start,mid]
* @param mid 中间位置 : leftArr[start,mid] 与rightArr[mid+1,end]
* @param end 区间右边界: rightArr[mid+1,end]
* @param tmp 临时数组
*/
public void merge(int[] arr, int start, int mid, int end, int[] tmp) {
int leftIndex = start; // 左数组leftArr[start,mid]的元素指针初始化
int rightIndex = mid + 1; //右数组rightArr[mid+1,end]的元素指针初始化
int index = 0; //tmp数组存储指针初始化
// 下面的逻辑是, 将leftArr,rightArr的每一个元素, 排序合并到tmp中(归并算法名字的由来)
// 循环1, 分别拿leftArr的当前指针指向元素与rightArr的当前指针指向元素比较大小, 取小的放入tmp的index指针位置
while (leftIndex <= mid && rightIndex <= end) {
if (arr[leftIndex] < arr[rightIndex]) {
tmp[index++] = arr[leftIndex++]; //取的谁, 就移动一下谁的指针(取的leftArr) 同时index也++移动了一次指针
} else {
tmp[index++] = arr[rightIndex++]; //取的谁, 就移动一下谁的指针(取的rightArr)
}
}
//循环2,目的: leftArr的剩余元素拷贝到tmp (对应的情况, leftArr的值比rightArr中元素大, 有剩余, 就将剩余元素整体拷贝到tmp)
//有可能不执行此循环
while (leftIndex <= mid) {
tmp[index++] = arr[leftIndex++];
}
//循环3, 目的: rightArr的剩余元素拷贝到tmp (对应的情况, righttArr的值比leftArr中元素大, 有剩余, 就将剩余元素整体拷贝到tmp)
//有可能不执行此循环,循环3与循环2只有一个会执行
while (rightIndex <= end) {
tmp[index++] = arr[rightIndex++];
}
//最终关键一步: 拷贝tmp数组元素到arr数组中, arr即为结果
System.arraycopy(tmp,0,arr,start,tmp.length);
}
/**
* 该处为测试代码
*/
@Test
public void testSort() {
int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1};
sort(arr);
for (int i : arr) {
System.out.println(i);
}
System.out.println("-------------------");
arr = new int[]{1};
sort(arr);
for (int i : arr) {
System.out.println(i);
}
System.out.println("-------------------");
arr = new int[]{};
sort(arr);
for (int i : arr) {
System.out.println(i);
}
}
}
关于递归代码的技巧: 代码注释中也有标注, 记住两点: 1, 写好递推公式; 2, 找到递归终止条件;基本的递归思路就完成了, 剩下的是把递归公式翻译成为代码.(当然还要注意递归深度问题)
排序算法快捷进入