基本思想
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子
序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序
表,称为二路归并。——黑马程序员
自己的理解
将一个数组分为两个已经排序好的数组,然后按照顺序一一进行大小对比,然后将两个数组合并到一个数组中。。。(胡言乱语了)
动画演示
排序的活动就如同示例图所示这般。
每次都将数组分为左右两部分,先将左部分的数据排好顺序后再排右部分数据的顺序,两部分都拍好之后进行归并。
第一次分为左右部分数据时,左部分右是一个新的数组,右部分也是一个新的数组,再分别进行分左右部分的操作(所以需要使用递归)。
直到左右数组中都只有一个数据时递归结束(此时左右的数据都只有一个,那么一定是有序的)
因为有序,所以可以进行归并操作。
此时左边的数据已被归并完毕,然后再进行右边数据的归并操作,最后再进行左右的数据的归并操作,依次反复直到整个数组有序。
归并排序示例
package com.stu.algorithm.copy;
import java.util.Arrays;
/**
* @version:
* @author: 零乘一
* @description: 类的简介
* @date: 2021/9/29 22:37
**/
public class Merge {
public static int[] temp;
public static void main(String[] args) {
int a[] = {8,4,5,7,1,3,6,2};
sort(a);
System.out.println(Arrays.toString(a));
}
public static void sort(int[] a){
//数组最左边的元素的下标
int l = 0;
//数组最右边的元素的下标
int r = a.length - 1;
//为临时存储的数组空间赋值
temp = new int[a.length];
sort(l,r,a);
}
public static void sort(int l, int r, int a[]){
if (l>=r){//当左下标大于等于右下标时直接返回
return ;
}
int mid = l + (r - l)/2;
//对l到mid之间的元素进行排序
sort(l,mid,a);
//对mid到r之间的元素进行排序
sort(mid+1,r,a);
//进行两两归并
merge(l,mid,r,a);
}
public static void merge(int l, int mid, int r, int a[]){
//指向临时数组的下标
int i = l;
//指向左半边数组的下标
int p1 = l;
//指向右半边数组的下标
int p2 =mid + 1;
//当两边的数组都还没归并结束时持续循环
while (p1 <= mid && p2 <= r){
if (a[p1] < a[p2]){
temp[i++] = a[p1++];
}else {
temp[i++] = a[p2++];
}
}
//假设p1指向的这部分数组中的数据没有全部归并,将数组中的数据都填入临时的数组中
while (p1<=mid){
temp[i++] = a[p1++];
}
while (p2<=r){
temp[i++] = a[p2++];
}
//将在临时数组中已经有序的数组写入正式的数组中
for (int j = l; j <= r; j++) {
a[j] = temp[j];
}
}
}
/*
测试数据:
int a[] = {8,4,5,7,1,3,6,2};
sort(a);
System.out.println(Arrays.toString(a));
*/
代码解析
还需要这两个循环是因为在归并时,总会有一半数组中的所有元素首先进入临时的数组中,另一个数组中还有值没有进入数组中,但我我们并不清除是哪个数组中的数据没有完全写入临时数组中,所以要写两个判断,但是这两个循环只会执行一个循环。
自己常出现的错误,在循环时总会忘记最后一个值(也就是循环条件是要等于最高位元素的下标)