归并排序的基本思想

归并排序是一种基于分治法(Divide and Conquer)的高效排序算法。它的核心思路可以概括为三个步骤:分解(Divide)递归排序(Conquer)合并(Merge)

  • 分解:将待排序的数组从中间分成左右两部分,直到每个子数组只包含一个元素(此时子数组本身就是有序的)。
  • 递归排序:对左右两个子数组分别递归执行归并排序,直到所有子数组都有序。
  • 合并:将两个已排序的子数组合并成一个更大的有序数组,最终得到整个数组的排序结果。

简单来说,归并排序就是先把大问题拆分成小问题,解决小问题后再把结果合并起来。

分解过程详解

假设我们有一个数组 [3, 1, 4, 2],归并排序的分解过程如下:

  1. 第一次分解:数组长度为4,中间位置是 4//2=2,所以分成左右两部分:left = [3, 1]right = [4, 2]
  2. 递归分解左子数组 [3, 1]:中间位置是 2//2=1,分成 left_left = [3]left_right = [1](每个子数组长度为1,无需再分解)。
  3. 递归分解右子数组 [4, 2]:中间位置是 2//2=1,分成 right_left = [4]right_right = [2](每个子数组长度为1,无需再分解)。

递归排序与合并

当分解到子数组长度为1时,子数组已经有序(单个元素本身就是有序的)。此时需要执行合并操作,将两个有序子数组合并成一个更大的有序数组。

以分解后的子数组为例:
- 左子数组合并:[3][1] 合并成 [1, 3]
- 右子数组合并:[4][2] 合并成 [2, 4]
- 最终合并:[1, 3][2, 4] 合并成 [1, 2, 3, 4]

Python代码实现

下面是归并排序的Python实现,分为合并函数递归排序函数两部分:

1. 合并函数 merge(left, right)

功能:合并两个已排序的子数组,返回一个新的有序数组。

def merge(left, right):
    merged = []  # 存储合并后的结果
    i = j = 0    # 两个子数组的指针(索引)

    # 比较两个子数组的元素,按顺序加入merged
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            merged.append(left[i])
            i += 1
        else:
            merged.append(right[j])
            j += 1

    # 将剩余未比较的元素直接加入(可能其中一个子数组已遍历完)
    merged.extend(left[i:])  # 添加left中剩余元素
    merged.extend(right[j:]) # 添加right中剩余元素
    return merged

2. 递归排序函数 merge_sort(arr)

功能:递归分解数组,调用合并函数得到排序结果。

def merge_sort(arr):
    # 基本情况:数组长度为1或0时,本身就是有序的
    if len(arr) <= 1:
        return arr

    # 分解数组:分成左右两部分
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])   # 递归排序左半部分
    right = merge_sort(arr[mid:])  # 递归排序右半部分

    # 合并两个已排序的子数组
    return merge(left, right)

测试代码

if __name__ == "__main__":
    test_array = [3, 1, 4, 2, 5]
    sorted_array = merge_sort(test_array)
    print("原始数组:", test_array)
    print("排序后数组:", sorted_array)

输出结果

原始数组: [3, 1, 4, 2, 5]
排序后数组: [1, 2, 3, 4, 5]

归并排序的特点

  • 时间复杂度:无论输入数组如何,归并排序的时间复杂度始终为 O(n log n)(分解和合并的总操作次数为O(n log n))。
  • 空间复杂度:需要额外的数组存储合并结果,空间复杂度为 O(n)
  • 稳定性:归并排序是稳定的排序算法(相等元素的相对顺序保持不变)。

归并排序的核心是“分而治之”和“合并有序数组”,通过递归分解和合并,最终实现整个数组的排序。虽然代码看起来比冒泡排序等简单排序算法复杂,但理解其思路后,会发现它非常直观且高效。

小夜