归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(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来自递归时额外的栈空间