[Go版]算法通关村第十关黄金——归并排序

目录

  • 归并排序(mergeSort)
    • 思路分析:二分分割 + 合并两个数组 + 递归
    • 遍历时处理元素的过程图:
    • 递归遍历时栈内的数据图:
    • 复杂度:时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)、空间复杂度 O ( n ) O(n) O(n)
    • Go代码
    • 比较:归并排序 和 快速排序
      • 快速排序:
      • 归并排序:

归并排序(mergeSort)

[Go版]算法通关村第十关黄金——归并排序_第1张图片

思路分析:二分分割 + 合并两个数组 + 递归

  1. 分割(Divide):将待排序的数组分割成两个子数组,将问题分解为更小的子问题。通常将数组分割成相等大小的两半,直到无法再分割(即数组中只剩下一个或零个元素)。
  2. 解决(Conquer):对每个子数组递归地进行排序。当子数组长度变得很小(通常为1)时,直接认为它们已经有序。
  3. 合并(Merge):将两个已排序的子数组合并为一个单一的、有序的数组。在合并过程中,通过逐一比较两个子数组中的元素,按照大小顺序将它们合并到一个新的数组中。

遍历时处理元素的过程图:

[Go版]算法通关村第十关黄金——归并排序_第2张图片

递归遍历时栈内的数据图:

[Go版]算法通关村第十关黄金——归并排序_第3张图片

复杂度:时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)、空间复杂度 O ( n ) O(n) O(n)

  • 时间复杂度:归并排序的时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn),其中 n 是数组的长度。归并排序将数组不断分割为两半,然后在合并的过程中将各个子数组进行合并操作。因为每次都是将两个有序的子数组合并,所以合并的时间复杂度是线性的,合并的次数是 O ( l o g n ) O(log n) O(logn)。因此,总体的时间复杂度是 O ( n l o g n ) O(n log n) O(nlogn)
  • 空间复杂度:在合并操作中使用了临时数组 tmp 来存储合并后的结果,其大小与原数组的大小相同。因此,空间复杂度是 O(n),其中 n 是数组的长度。

Go代码

func sortArray(nums []int) []int {
    length := len(nums)
    if length == 0 || length == 1 {
        return nums
    }
    // 初始化时创建tmp,然后在每次合并时复用这个数组,可避免在每次递归调用时创建和释放临时数组,从而减少空间的创建和释放次数
    tmp := make([]int, length)
    sortArr(nums, 0, length-1, tmp)
    return nums
}

func sortArr(nums []int, left int, right int, tmp []int) {
    if left>=right {
        return
    }
    mid := (right-left)>>1+left
    // 拆分数组
    sortArr(nums, left, mid, tmp)
    sortArr(nums, mid+1, right, tmp)
    i,j := left, mid+1
    x := 0
    // 合并到tmp
    for ;i<=mid&&j<=right; x++ {
        if nums[i] < nums[j] {
            tmp[x] = nums[i]
            i++
        } else {
            tmp[x] = nums[j]
            j++
        }
    }
    for ;i<=mid; x,i=x+1,i+1 {
        tmp[x] = nums[i]
    }
    for ;j<=right; x,j=x+1,j+1 {
        tmp[x] = nums[j]
    }
    // 从tmp赋值到nums
    for i:=0; i<=right-left; i++ {
        nums[i+left] = tmp[i]
    }
}

比较:归并排序 和 快速排序

快速排序:

平均时间复杂度:O(n log n)
最坏情况时间复杂度:O(n^2)(当划分不均匀时,例如已经有序的数据)
最好情况时间复杂度:O(n log n)(当每次划分都能平分数组)
不稳定排序:相等元素的相对顺序可能被改变
需要较少的额外内存

归并排序:

平均时间复杂度:O(n log n)
最坏情况时间复杂度:O(n log n)
最好情况时间复杂度:O(n log n)
稳定排序:相等元素的相对顺序不会改变
需要较多的额外内存(用于合并操作)

在实际应用中,快速排序通常比归并排序更快,因为它的常数因子较小,而且在一些情况下,归并排序的额外内存消耗可能限制了它的效率。然而,需要注意的是,快速排序的性能高度依赖于数据的分布和选取的枢轴(pivot)元素。如果选择的枢轴不好,快速排序的性能可能下降到最坏情况。

归并排序的性能相对稳定,适用于各种数据分布情况,并且在一些对内存消耗有限制的情况下也很有用。

综上所述,虽然快速排序通常更快,但要根据具体情况选择合适的排序算法,以满足不同的性能和内存需求。

你可能感兴趣的:(算法与数据结构,算法,排序算法)