6.8 归并排序
6.8.1归并排序介绍
归并排序(merge sort)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
6.8.2 基本思想
对于一个待排序的数组,首先进行分解,将整个待排序数组以mid中间位置为界,一分为二,随后接着分割,直至到最小单位无法分割;开始进行治的操作,将每两个小部分进行比较排序,并逐步合并;直至合并成整个数组的大小。从而完成了整个排序的过程。
以数据{8,4,5,7,1,3,6,2}为例,归并排序思想的示意图如下:
-
首先是分的步骤,将整个数组进行分割,分成了若干个单独的较小的单元。
-
然后是合并的过程,分别将分开的两个较小单元进行比较合并到原来的数组里面;将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],步骤如下图所示:
6.8.3 代码实现
package com.kevin.sortAlgorithm;
import java.util.Arrays;
/**
* @author : kevin ding
* @date : 2022/3/10 22:20
* @description : 归并排序 采用经典的分治策略,先将问题分解成一些笑的问题,然后递归求解
*/
public class MergeSortDemo {
public static void main(String[] args) {
int[] array = {
8,4,5,7,1,3,6,2};
int[] tempArray = new int[array.length];
System.out.println("merge排序之前:");
System.out.println(Arrays.toString(array));
mergeSort(array, 0, array.length-1, tempArray);
System.out.println("merge排序之后:");
System.out.println(Arrays.toString(array));
}
public static void mergeSort(int[] array, int left, int right, int[] tempArray){
// 先开始分,在合并。分之前 必须保证left < right才能分
if(left < right){
// 分之前 先先求解中间值
int mid = (left + right) / 2;
// 向左 接着分,以left为左边界,以mid为右边界
mergeSort(array, left, mid, tempArray);
// 向右接着分 以mid+1为左边界,right为右边界
mergeSort(array, mid+1, right, tempArray);
// 分完之后,开始合并
merge(array, left, mid, right, tempArray);
}
}
/**
*
* @param array 待排序的数组
* @param left 待排序数组的左边界
* @param mid 待排序数组的中间值 (左右两个有序序列的界限)
* @param right 待排序数组的右边界
* @param tempArray 临时数组,存放部分排序好的数据
*/
public static void merge(int[] array, int left, int mid, int right, int[] tempArray){
int leftIndex = left; // 左边有序数组的索引
int rightIndex = mid+1; // 右边有序数组的索引
int tempIndex = 0; // 定义临时数组的索引
// 1. 将左右两边有序数组中的数,按照大小依次放入的tempArray中,直到其中一个有序数组放入完毕
// 左边有序数组的右边界为mid,右边数组的右边界为right,只要任一索引到边界,则遍历结束
while (leftIndex <= mid && rightIndex <= right){
if(array[leftIndex] <= array[rightIndex]){
// 左边序列的当前值小,将其存入tempArray中,随后将索引分别后移一位
tempArray[tempIndex] = array[leftIndex];
tempIndex += 1;
leftIndex += 1;
}else{
// 否则,右边序列的当前值较小,存入tempArray中
tempArray[tempIndex] = array[rightIndex];
tempIndex += 1;
rightIndex += 1;
}
}
// 2. 将剩余一个有序数组的值全部依次放入到临时数组tempArray中
// 左边有序数组还有剩余
while(leftIndex <= mid){
tempArray[tempIndex] = array[leftIndex];
tempIndex += 1;
leftIndex += 1;
}
// 右边有序数组还有剩余
while(rightIndex <= right){
tempArray[tempIndex] = array[rightIndex];
tempIndex += 1;
rightIndex += 1;
}
// 3.将临时数组中已排好序的值,再拷贝回原数组中
// 拷贝回去时,tempIndex每次都是从零开始,因为每次合并的时候都是从零位置开始往里放
tempIndex = 0;
// 往回拷贝,并非每次都是拷贝所有,是根据传入的参数left和right来定的
int tempLeftIndex = left;
// 开始拷贝,从left到right
while(tempLeftIndex <= right){
array[tempLeftIndex] = tempArray[tempIndex];
tempLeftIndex += 1;
tempIndex += 1;
}
}
}