基本思想
采用经典的分治策略,先递归地将当前数组平均分成两半,然后将有序数组两两合并,最终合并成一个有序数组。
算法步骤
假设数组的元素个数为 n 个,则归并排序的算法步骤如下:
- 分解过程:先递归地将当前数组平均分成两半,直到子数组长度为 1。
- 找到数组中心位置 mid,从中心位置将数组分成左右两个子数组 left_nums、right_nums。
- 对左右两个子数组 left_nums、right_nums 分别进行递归分解。
- 最终将数组分解为 n 个长度均为 1 的有序子数组。
- 归并过程:从长度为 1 的有序子数组开始,依次将有序数组两两合并,直到合并成一个长度为 n 的有序数组。
- 使用数组变量 nums 存放合并后的有序数组。
- 使用两个指针 left_i、right_i 分别指向两个有序子数组 left_nums、right_nums 的开始位置。
- 比较两个指针指向的元素,将两个有序子数组中较小元素依次存入到结果数组 nums 中,并将指针移动到下一位置。
- 重复步骤 3,直到某一指针到达子数组末尾。
- 将另一个子数组中的剩余元素存入到结果数组 nums 中。
- 返回合并后的有序数组 nums。
我们以 [0,5,7,3,1,6,8,4] 为例,演示一下归并排序算法的整个步骤。
适用场景
大规模数据排序,要求稳定性的排序需求,外部排序,并行计算环境
排序稳定性
因为在两个有序子数组的归并过程中,如果两个有序数组中出现相等元素,merge(left_nums, right_nums):
算法能够使前一个数组中那个相等元素先被复制,从而确保这两个元素的相对顺序不发生改变。因此,归并排序算法是一种 稳定排序算法。
代码实现(golang)
// 归并排序
func MergeSort(arr []int) []int {
if len(arr) <= 1 {
return arr
}
mid := len(arr) / 2
left_arr := MergeSort(arr[:mid])
right_arr := MergeSort(arr[mid:])
arr1 := sort(left_arr, right_arr)
return arr1
}
func sort(left []int, right []int) []int {
var result []int
// 将两个有序子数组中较小元素依次插入到结果数组中
l, r := 0, 0
for l < len(left) && r < len(right) {
if left[l] < right[r] {
result = append(result, left[l])
l++
} else {
result = append(result, right[r])
r++
}
}
if l < len(left) {
for l < len(left) {
result = append(result, left[l])
l++
}
}
if r < len(right) {
for r < len(right) {
result = append(result, right[r])
r++
}
}
return result
}
func main() {
arr := []int{4, 2, 2, 8, 3, 3, 1}
sortedArr := MergeSort(arr)
fmt.Println(sortedArr)