归并排序采用的是分治策略思想
归并排序是递归排序,思路是将数据表持续分裂为两半,对两半分别进行归并排序
递归的基本结束条件是:数据表仅有1个数据项,自然是排好序的
缩小规模:将数据表分裂为相等的两半,规模减为原来的二分之一
调用自身:将两半分别调用自身排序,然后将分别排好序的两半进行归并,得到排好序的数据表
归并排序:图示
归并排序:代码
package com.zyj.test; import java.util.Arrays; /** * 归并排序算法实现和分析 * * @author 张永俊 */ public class DemoTest { public static void main(String[] args) { int[] arr = new int[]{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; int[] temp = new int[arr.length]; mergeSort(arr, temp, 0, arr.length - 1); System.out.println(Arrays.toString(temp)); } public static void mergeSort(int[] arr, int[] temp, int left, int right) { /*判断左是否小于右指针,如果条件不成立则表示数据表只有一个数值*/ if (left < right) { /*获取中间值*/ int middle = (left + right) >> 1; /*将数据表拆分成左半部分和右半部分*/ mergeSort(arr, temp, left, middle); mergeSort(arr, temp, middle + 1, right); /*此时左半部分和右半部分都是有序的,对两部分进行汇和*/ merge(arr, temp, left, middle, right); } } public static void merge(int[] arr, int[] temp, int left, int middle, int right) { /*定义左半部分遍历起始位置*/ int low1 = left; /*定义右半部分遍历起始位置*/ int low2 = middle + 1; /*定义临时数据初始位置*/ int pos = left; /*判断两部分指针的位置,有任何一方到达数据表尾部,停止循环*/ while (low1 <= middle && low2 <= right) { /*升序排列小的放前,大的放后*/ if (arr[low1] < arr[low2]) { temp[pos++] = arr[low1++]; } else { temp[pos++] = arr[low2++]; } } /*如果左半部分没有执行完,则继续执行*/ while (low1 <= middle) { temp[pos++] = arr[low1++]; } /*如果右半部分没有执行完,则继续执行*/ while (low2 <= right) { temp[pos++] = arr[low2++]; } /*将存在临时表的数据覆盖到原始数据表,进行下一轮排序*/ while (left <= right) { arr[left] = temp[left++]; } } }
输出结果:
归并排序:算法分析
将归并排序分为两个过程来分析:分裂和归并
分裂的过程,借鉴二分查找中的分析结果,是对数复杂度,时间复杂度为O(log n)
归并的过程,相对于分裂的每个部分,其所有数据项都会被比较和放置一次,所以是线性复杂度,其时间是O(n)
综合考虑,每次分裂的部分都进行一次O(n)的数据项归并,总的时间复杂度是O(nlog n)
最后,我们还是注意到两个切片操作
为了时间复杂度分析准确起见,可以通过取消切片操作,改为传递两个分裂部分的起始点和终止点,也是没问题的,只是算法可读性稍微牺牲一点点。
我们注意到归并排序算法使用了额外1倍的存储空间用于归并
这个特性在对待大数据集进行排序的时候要考虑进去
所以在Hadoop中MapReduce都是在各自分区中先进行快速排序,Combiner阶段再进行归并排序,节约内存资源