1. 要点
分而治之的思想,使用递归,先拆分在合并(排序是在合并时候完成)
算法灵感:张三比李四大,李四比王五大,求他们的年龄的顺序?
张三 > 李四 > 王五
(这就是进行合并时候排序的核心思想)
2.图演示
2.1 过程图展
这是一个 [5,1,9,3,7,4,8,6,2]
的例子
要点:先拆分,再合并排序
2.2 动图演示
3.算法步骤
- 1.先递归分而治之,将数组分为-长度为1的小数组。
- 2.将2个长度为1的数组排序并组合为一个新的一个长度为2的数组,将这一层所有的元素组合完成
- 3.将2个长度为2的数组组合排序为一个新的长度为4的数组,将这一层的元素组合完成.
- 4.重复2,3步骤,直到恢复到原来的长度
组合时候可能遇到总数为奇数的情况,比如上图中的9个元素的数组,处理参考 2.1的过程图展示的,1,5 元素,只需要在加一层将他们分开即可,当然合并的时候 1,5 元素也要多一层计算.
4.代码
java 代码实现
public static int[] mergeSort(int[] sourceArray){
int length = sourceArray.length;
//只有一个元素时候停止递归
if (length <= 1){
return sourceArray;
}
//因为失去精度,这样取出的中间值会让奇数的length,leftArray分得多一个的元素
int middle = length - length/2;
int[] leftArray = Arrays.copyOfRange(sourceArray,0,middle);
int[] rightArray = Arrays.copyOfRange(sourceArray,middle,length);
return merge(mergeSort(leftArray),mergeSort(rightArray));
}
private static int[] merge(int[] leftArray,int[] rightArray){
int[] merge = new int[leftArray.length+rightArray.length];
//开始组合并排序
int indexOfMerge = 0;
while (leftArray.length > 0 && rightArray.length > 0){
if (leftArray[0] < rightArray[0]){
merge[indexOfMerge++] = leftArray[0];
leftArray = Arrays.copyOfRange(leftArray,1,leftArray.length);
}else {
merge[indexOfMerge++] = rightArray[0];
rightArray = Arrays.copyOfRange(rightArray,1,rightArray.length);
}
}
//排列剩下的左边的元素left
while (leftArray.length > 0 ){
merge[indexOfMerge++] = leftArray[0];
leftArray = Arrays.copyOfRange(leftArray,1,leftArray.length);
}
//排列剩下的右边的元素.
while (rightArray.length >0 ){
merge [indexOfMerge ++] = rightArray[0];
rightArray = Arrays.copyOfRange(rightArray,1,rightArray.length);
}
return merge;
}
go 版本
//分
func divide(array []int) []int {
if len(array) <= 1 {
return array
}
middle := len(array) / 2
left_array := array[:middle]
right_array := array[middle:]
//合并.
return merge(divide(left_array), divide(right_array))
}
//合并
//排序在合并时候进行.
func merge(left_array, right_array []int) []int {
result_merge := make([]int, 0)
//左边的长度始终要小于等于右边,知道将左边的元素全部 merge.
for len(left_array) > 0 {
if len(right_array) == 0 || left_array[0] < right_array[0] {
result_merge = append(result_merge, left_array[0])
left_array = left_array[1:]
} else {
result_merge = append(result_merge, right_array[0])
right_array = right_array[1:]
}
}
//merge 右边的的元素
for len(right_array) > 0 {
result_merge = append(result_merge, right_array[0])
right_array = right_array[1:]
}
return result_merge
}
测试
old_array := []int{3, 4, 1, 8, 9, 2, 5, 6, 7}
if old_array==nil || len(old_array)<=1 {
return nil,err
}
//1.未排序的数组
fmt.Printf("old_array=%#v\n", old_array)
//开始排序
new_array = divide(old_array)
fmt.Printf("new_array=%#v\n", new_array)
优化内存分配
最佳到容器非导致多次扩容的内存分配, 我们可以一次性构造合适大小的数组减少内存分配
//归并排序
//拆分, 排序, 合并返回排序号的数组
func MergeSort2(arr []int) (respArr []int) {
if len(arr) <= 1 {
return arr
}
mid := len(arr) / 2
return merge2(MergeSort2(arr[:mid]), MergeSort2(arr[mid:]))
}
//排序,合并
func merge2(left, right []int) (respArr []int) {
respArr = make([]int, len(left)+len(right))
l, r := 0, 0
for i := 0; i < len(respArr); i++ {
if r == len(right) {
//如果右边数组用光了
respArr[i] = left[l]
l++
} else if l == len(left) {
//如果左边的数组用光了
respArr[i] = right[r]
r++
} else {
//如果都没有用光
if left[l] <= right[r] {
respArr[i] = left[l]
l++
} else {
respArr[i] = right[r]
r++
}
}
}
return respArr
}
5.优缺点
优点:
- 快速 O(n3/2)
缺点:
- 调用大量的方法和递归,浪费内存.
- 不稳定:因为无法保证相同的元素排序后的相对位置,因此这是不稳定的排序算法.