白话归并排序

0 阅读2分钟

核心原理

分分合合; 分:数组从中间不断拆分,直到都剩单个元素的数组(有序数组); 合:拿两组有序小数组,从头挨个比大小,小的先放进新数组,拼成一个大有序数组,层层往上合并;

通俗步骤

  1. 拆分:对半劈开,拆到只剩一个数;
  2. 合并:两个有序小数组,小头对比拣小;
  3. 再合并大数组;
  4. 不断合并直到全部有序;

举例演示 [3,1,4,2]

中间拆分

[3,1,4,2] -> [3,1] [4,2] -> [3] [1] [4] [2]

合并过程

[3] [1] -> [1,3] [4] [2] -> [2,4]

再合并

1 < 2 → 取 1;剩 [3]、[2,4] -> [1] 3 > 2 → 取 2;剩 [3]、[4] -> [1,2] 3 < 4 → 取 3;最后剩 4 补上 -> [1,2,3,4]

代码示例

package main

import (
 "fmt"
)

func main() {
 arr := []int{3, 1, 4, 2}
 newArr := mergeSort(arr)
 fmt.Println(arr)    // [3, 1, 4, 2]
 fmt.Println(newArr) // [1, 2, 3, 4]
}

// 递归拆分
func mergeSort(arr []int) []int {
 if len(arr) <= 1 {
  return arr
 }
 mid := len(arr) / 2
 left := mergeSort(arr[:mid])
 fmt.Println("left:", left)
 right := mergeSort(arr[mid:])
 fmt.Println("right:", right)
 return merge(left, right)
}

// 合并
func merge(left, right []int) []int {
 res := make([]int, 0, len(left)+len(right))
 i, j := 0, 0
 // 挨个取小值
 for i < len(left) && j < len(right) {
  if left[i] < right[j] {
   res = append(res, left[i])
   i++
  } else {
   res = append(res, right[j])
   j++
  }
 }
 // 余下数据直接追加
 for i < len(left) {
  res = append(res, left[i])
  i++
 }
 for j < len(right) {
  res = append(res, right[j])
  j++
 }
 return res
}

递归拆分流程

mergeSort([3, 1, 4, 2])
│
├─ mid = 2
├─ left = mergeSort([3, 1])          ← 先递归左半
│       │
│       ├─ mid = 1
│       ├─ left = mergeSort([3])     ← 基线,返回 [3]
│       │   输出: left: [3]
│       ├─ right = mergeSort([1])    ← 基线,返回 [1]
│       │   输出: right: [1]
│       └─ return merge([3], [1]) → [1, 3]
│           输出: left: [1, 3]       ← 这层 left 完成
│
├─ right = mergeSort([4, 2])         ← 再递归右半
│       │
│       ├─ mid = 1
│       ├─ left = mergeSort([4])     ← 基线,返回 [4]
│       │   输出: left: [4]
│       ├─ right = mergeSort([2])    ← 基线,返回 [2]
│       │   输出: right: [2]
│       └─ return merge([4], [2]) → [2, 4]
│           输出: right: [2, 4]      ← 这层 right 完成
│
└─ return merge([1, 3], [2, 4]) → [1, 2, 3, 4]

合并流程

初始:  left = [1, 3, 5]    right = [2, 4, 6]    res = []
             i=0                   j=0

┌──────┬───────────┬────────────┬──────────────────┬────┐
│ 轮次 │ left[i]   │ right[j]   │ 比较             │ res    │
├──────┼───────────┼────────────┼──────────────────┼────┤
│  1   │ 1         │ 2          │ 1 < 2 → 取1, i++ │ [1]    │
│  2   │ 3         │ 2          │ 3 > 2 → 取2, j++ │ [1,2]  │
│  3   │ 3         │ 4          │ 3 < 4 → 取3, i++ │ [1,2,3]│
│  4   │ 5         │ 4          │ 5 > 4 → 取4, j++ │ [1,2,3,4]│
│  5   │ 5         │ 6          │ 5 < 6 → 取5, i++ │ [1,2,3,4,5]│
└──────┴───────────┴────────────┴──────────────────┴────┘
→ i=3 越界,主循环退出

收尾: right 还剩 [6],追加 → res = [1,2,3,4,5,6]