归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
基本思路:
先递归的把数组划分为两个子数组,一直递归到数组中只有一个元素,然后再调用函数把两个子数组排好序,因为该函数在递归划分数组时会被压入栈,所以这个函数真正的作用是对两个有序的子数组进行排序;
基本步骤:
1、判断参数的有效性,也就是递归的出口;
2、首先什么都不管,直接把数组平分成两个子数组;
3、递归调用划分数组函数,最后划分到数组中只有一个元素,这也意味着数组是有序的了;
4、然后调用排序函数,把两个有序的数组合并成一个有序的数组;
5、排序函数的步骤,让两个数组的元素进行比较,把大的/小的元素存放到临时数组中,如果有一个数组的元素被取光了,那就直接把另一数组的元素放到临时数组中,然后把临时数组中的元素都复制到实际的数组中;
/**
* 此递归排序的merge,并不是将数组a,b合并成c
* 而是在原数组上,将下标left~mid与mid+1~right的两个数据段(相当于两个数组)合并成一个有序数组,再拷贝到原数组的对应位置,这都是在对原数组操作
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = {9,8,7,6,5,4,3,2,1};
mergeSort(arr);
for (int i = 0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
public void mergeSort(int[] arr){
sort(arr,0,arr.length-1);
}
public static void sort(int[] arr,int left,int right){
if (left < right){
int mid = (left+right)/2;
//先递归排序左边
sort(arr,left,mid);
//再递归排序右边
sort(arr,mid+1,right);
//合并两边排序后结果
merge(arr,left,mid,right);
}
}
/**
* @desc 将同一个数组中的两段连续数据排序,前提,两段数据已经有序(从小到达)
* 例如:1,3,5,7,9,2,4,6,8,
* 从下标0到7排序后,1,2,3,4,5,6,7,8
* 从下标3,6排序后,1,3,5,2,4,7,9,6,8
* @param arr
* @param left
* @param mid
* @param right
* @return
*/
public static void merge(int[] arr,int left,int mid,int right){
//将两小段合并后的有序数组
int[] r = new int[right-left+1];
//r中的下标
int k = 0;
//第一段数组从left开始
int i = left;
//第二段数组从right开始
int j = mid + 1;
//循环条件:第一段数组和第二段数组下标都不超过各自的最后一个元素下标
while (i <= mid && j <= right){
if (arr[i] <= arr[j]){
r[k++] = arr[i++];
}else {
r[k++] = arr[j++];
}
}
//当两端数组的长度不想等时,可能存在其中一组还未被遍历到的情况,直接将其拷贝进r即可
while (i <= mid){
r[k++] = arr[i++];
}
//当两端数组的长度不想等时,可能存在其中一组还未被遍历到的情况,直接将其拷贝进r即可
while (j <= right){
r[k++] = arr[j++];
}
k = 0;
//将已排好序的部分覆盖掉原数组的对应部分
while (left <= right){
arr[left++] = r[k++];
}
}
}
参考:https://www.cnblogs.com/chengxiao/p/6194356.html
归并排序是一种比较占内存,但却高效且稳定的算法。
时间复杂度:O(nlogn)
空间复杂度:O(n+logn),logn来自递归时额外的栈空间