归并排序是一个典型的基于分治的递归算法。它不断地将原数组分成大小相等的两个子数组,直到划分的子数组大小为1的过程。此算法操作即为:分。然后将划分的有序子数组合并成一个更大的有序数组,此算法操作即为:治。
它将数组平均分成两部分: mid= (left + right)/2,当数组分得足够小时,即:数组中只有一个元素时,只有一个元素的数组自然而然地就可以视为是有序的,此时就可以进行合并操作了。因此,合并两个有序的子数组,是从 只有一个元素 的两个子数组开始合并的。
比如初始数组:[8, 4, 5, 7, 1, 3, 6, 2]
①分成了两个大小相等的子数组:[8, 4, 5, 7] [1, 3, 6, 2]
②再划分成了四个大小相等的子数组:[8, 4] [5, 7] [1, 3] [6, 2]
③此时,left < right 还是成立,再分:[8] [4] [5] [7] [1] [3] [6] [2]
此时,有8个小数组,每个数组都可以视为有序的数组了!!!
第一轮合并
merge([8],[4]) 得到 [4,8]
merge([5],[7]) 得到[5,7]
merge([1],[3]) 得到[1,3]
merge([6],[2]) 得到[2,6]
第二轮合并
merge( [4,8],[5,7]) 得到 [4,5,7,8]
merge( [1,3],[2,6]) 得到 [1,2,3,6]
第三轮合并
merge( [4,5,7,8], [1,2,3,6]) 得到 [1,2,3,4,5,6,7,8]
最终得到 有序数组 [1,2,3,4,5,6,7,8]。
以下为图示(分)过程:
以下为图示(治)过程:
1<4将1填入临时数组
2<4将2填入临时数组
3<4将3填入临时数组
4<8将4填入临时数组
5<8将5填入临时数组
7<8将7填入临时数组
8<9将8填入临时数组
只剩下9,将9填入数组的最后位置
最终数组
实现说明:
① 以mid为分隔线分别向左、向右递归拆分数组。
②创建一个临时数组temp,用来保存两个子数组临时合并的结果。
③使用while循环,当左边的数组元素小于等于mid且右边的数组元素小于等于最右侧下标时,就将左边的元素填充到 临时数组temp,且左边数组下标后移,temp数组下标后移。否则,将右边的元素填充到 临时数组temp,且右边数组下标后移,temp数组下标后移。
④不满足上述条件时,如果左边的数组剩余元素的下标小于等于mid,则代表左侧数组还有剩余元素未填充到temp中,此时循环将剩余的左边数组元素填充到temp中。如果右边的数组剩余元素的下标小于等于最右侧下标时,则代表右侧数组还有剩余元素未填充到temp中,此时循环将剩余的右边数组元素填充到temp中。
⑤合并完成,此时临时数组中的元素为有序的数组,将临时数组复制到指定排序的数组arr中即可。
代码实现:
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int [] arr = {8, 4, 5, 7, 1, 3, 6, 2};
int temp[] = new int[arr.length];
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, right, mid, temp);
}
}
public static void merge(int[]arr,int left,int right,int mid,int[] temp){
int index=0; //temp数组下标
int i = left;//左边开始下标
int j = mid+1; //右边开始下标
//第一步,如果左边的数小于右边的数,就将左边的数填充到temp数组中,否则将右边的数填充到temp数线中
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[index] = arr[i];
index++;
i++;
}else{
temp[index] = arr[j];
index++;
j++;
}
}
//第二步,将剩下的数填充到temp数组中
while (i<=mid){
temp[index] = arr[i];
index++;
i++;
}
while (j<=right){
temp[index] = arr[j];
index++;
j++;
}
//第三步,将临时数组temp 复制到arr中
int templeft = left;
index =0;
while (templeft<=right){
arr[templeft] = temp[index];
templeft++;
index++;
}
}
}
输出结果:
[1, 2, 3, 4, 5, 6, 7, 8]