归并排序--分而治之

介绍

归并排序是非常经典的排序算法之一,相比于冒泡排序、插入排序、选择排序等方法,在时间复杂度上具有很大的优势,适用于大规模的数据排序。归并排序算法很好的利用了分治策略的思想,将一个问题分为若干个子问题,再把子问题看做一个问题继续划分,直至问题的规模足够小,此时解决该子问题的时间复杂度为常数。分治策略中,我们递归的求解一个问题,每次递归的的步骤为:

  • 分解:将问题划分为若干子问题,子问题的形式与原问题相同,但规模更小。
  • 求解:递归的求解子问题。若子问题规模足够小,则直接求解。
  • 合并:将每个子问题的结果合并为整个问题的解。

对于归并排序算法,完全适用于以上的求解步骤。对于规模为n的数组,每次递归地分为原来的1/2,直至划分的子数组规模为1,则直接返回子数组本身,再将求解出的子问题合并,即将两个有序数组合并成一个有序数组。

代码

def MergeSort(arr: List[int], p: int, r: int):
    # 将一个数组的左右两个有序子数组合并成一个有序数组
    def Merge(arr: List[int], p: int, q: int, r: int):
        arr1 = arr[p:q + 1]
        arr2 = arr[q + 1:r + 1]

        p1, p2 = 0, 0

        for i in range(p, r + 1):
            if arr1[p1] < arr2[p2]:
                arr[i] = arr1[p1]
                p1 += 1
            else:
                arr[i] = arr2[p2]
                p2 += 1

            # 判断是否其中一个子数组被完全复制到了原数组中,若是,则将另一个子数组按顺序直接复制到原数组中
            if p1 == q - p + 1:
                arr[i + 1:r + 1] = arr2[p2:]
                break
            elif p2 == r - q:
                arr[i + 1:r + 1] = arr1[p1:]
                break

    # 当p>=r时,子数组中仅有一个元素,不进行操作,即子问题划分得足够小,直接求解。
    if p < r:
        q = (p + r) // 2
        Merge_sort(arr, p, q)
        Merge_sort(arr, q + 1, r)
        Merge(arr, p, q, r)

对于规模为n的数组,则初始得调用代码为:

MergeSort(arr,0,n-1)

时间复杂度

从MergeSort函数可以看出,里面递归调用了两次MergeSort,传入数组的规模是原来的一半,即左右两个部分,进行了分解和求解两个步骤,然后调用了Merge函数,在最坏的情况下,Merge函数的时间复杂度为O(n),则在最坏的情况下,整体算法的时间复杂度可以用递归式表示为:
归并排序算法时间复杂度递归公式
当数组规模为1时,复杂度为常数,当规模大于1,则表示为分解的两个子问题的时间复杂度和合并所用时间复杂度的和。求解该递归公式,最终算法所需要的时间代价为:cnlgn+cn,其中c为常系数。则归并排序的时间复杂度为:

具体的求解步骤请参考书籍 算法导论 第一部分2.3.2节。

该文章部分引用于算法导论一书

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值