算法-基本排序算法-05 归并排序详解

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)

缺点:

  • 调用大量的方法和递归,浪费内存.
  • 不稳定:因为无法保证相同的元素排序后的相对位置,因此这是不稳定的排序算法.

6.拓展

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JarvanStack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值