零、写在前面
- 活动地址:CSDN21天学习挑战赛
- 本人蒟蒻一枚,文章若有不足之处请大家批评指出,欢迎大家留言。
一、排序算法
排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素的任意序列,重新排列成一个关键字有序的序列。
二、归并排序 (Merge Sort)
1. 介绍
归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治( divide
and conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案修补”在一起,即分而治之)。
1.1 算法描述
- 把长度为n的输入序列分成两个长度为n/2的子序列;
- 对这两个子序列分别采用归并排序;
- 将两个排序好的子序列合并成一个最终的排序序列。
1.2 算法步骤
现在有一个数组 arr = [8, 3, 4, 7, 1, 6, 2, 5],将其排序。
- 我们先将数组划分成两个子数组;
- 一直递归,把子数组划分成更小的子数组,直到子数组只有一个元素;
- 按照递归的返回顺序,不断合并排序好的子数组,直到整个数组有序。
1.3 过程分析
如图所示实现划分
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) { //(1)
int mid = (left + right) / 2; //(2)
mergeSort(arr, left, mid, temp); //(3)
mergeSort(arr, mid + 1, right, temp); //(4)
merge(arr,left,mid,right,temp); //(5)
}
}
(1)当 left 小于 right,说明元素个数大于1,将其划分成两个子数组
(2)位于中间元素索引
(3)左子数组
(4)右子数组
(5)合并左右子数组
合并过程,对最后一组分析,其他同理
(1)设置变量 i 指向有序左子数组第一个值的索引;j 右子数组索引;t 存放合并后数组的索引
(2)比较 arr[i] 与 arr[j] 值的大小,数值小的放入 temp[t];
(3)左右子数组中数值小的索引加一, t++; 重复(2),直到 i 和 j 遍历完
例如,本图 j 指向的值 小于 i 指向的,,j++,t++。
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
// (1)
int i = left;
int j = mid+1;
int t = 0;
while (i <= mid & j <= right) {
if (arr[i] <= arr[j]) { // (2)(3)
temp[t] = arr[i];
t += 1;
i += 1;
}else{
temp[t] = arr[j];
t += 1;
j += 1;
}
}
while( i <= mid ) {//左子数组还有剩余的元素,就全部填充到temp
temp[t] = arr[i];
t += 1;
i += 1;
}
while( j <= right ) {//右子数组还有剩余的元素,就全部填充到temp
temp[t] = arr[j];
t += 1;
j += 1;
}
//将temp数组的元素拷贝到arr
t = 0;
int tempLeft = left;
while( tempLeft <= right ) {
arr[tempLeft] = temp[t];
t += 1;
tempLeft +=1;
}
}
1.3 复杂度分析
归并排序是一种稳定的排序方法。归并排序的性能不受输入数据的影响,始终都是O(nlogn)的时间复杂度。
- 时间复杂度:O(nlogn)。
- 空间复杂度:O(n)。因为需要额外 n 大小的数组用于合并。
1.4 完整代码
package drs.merge.sort;
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int arr[] = {8,3,4,7,1,6,2,5};
int temp[] = new int[arr.length];
System.out.println("排序前数组:"+ Arrays.toString(arr));
mergeSort(arr, 0, arr.length-1, temp);
System.out.println("排序后数组:"+ Arrays.toString(arr));
}
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr,left,mid,right,temp);
System.out.println("排序中数组:"+ Arrays.toString(arr));
} else {
return;
}
}
/**
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转的数组
**/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;
int j = mid+1;
int t = 0;
while (i <= mid & j <= right) {
if (arr[i] <= arr[j]) {
temp[t] = arr[i];
t += 1;
i += 1;
}else{
temp[t] = arr[j];
t += 1;
j += 1;
}
}
while( i <= mid ) {
temp[t] = arr[i];
t += 1;
i += 1;
}
while( j <= right ) {
temp[t] = arr[j];
t += 1;
j += 1;
}
t = 0;
int tempLeft = left;
while( tempLeft <= right ) {
arr[tempLeft] = temp[t];
t += 1;
tempLeft +=1;
}
}
}
运行结果:
排序前数组:[8, 3, 4, 7, 1, 6, 2, 5]
排序中数组:[3, 8, 4, 7, 1, 6, 2, 5]
排序中数组:[3, 8, 4, 7, 1, 6, 2, 5]
排序中数组:[3, 4, 7, 8, 1, 6, 2, 5]
排序中数组:[3, 4, 7, 8, 1, 6, 2, 5]
排序中数组:[3, 4, 7, 8, 1, 6, 2, 5]
排序中数组:[3, 4, 7, 8, 1, 2, 5, 6]
排序中数组:[1, 2, 3, 4, 5, 6, 7, 8]
排序后数组:[1, 2, 3, 4, 5, 6, 7, 8]
ps:参考资料
- 一头小山猪. 经典算法. https://blog.csdn.net/u012039040/category_10804722.html
- 一像素. 十大经典排序算法(动图演示). https://www.cnblogs.com/onepixel/p/7674659.html
- 菜鸟教程. 十大经典排序算法. https://www.runoob.com/w3cnote/merge-sort.html
- 一条为归的鱼. 归并排序. https://blog.csdn.net/weixin_45857153/article/details/110474615