Python实现归并排序

就像前面说的那样,分而治之是一种通常能够开发出高效算法的技术。假设你和一个朋友一起尝试把一副扑克牌按照顺序进行排序。你可以通过将这副牌分成两半来分解问题,这样的话,每个人都将会对其中一半的扑克牌进行排序。这样一来,你就只需要再去找出一个能够组合两个有序堆的方法。

将两个有序列表组合成单个有序结果的过程被称为归并(merging)。因此,对于我们分而治之的算法来说,它被称为归并排序(mergesort)。这个算法如下所示:

把数组分成两部分
对左半部分排序
对右半部分排序
把排序好的两部分合并

算法的第一步非常简单,我们可以使用列表的切片方法来处理它。算法的最后一步是将列表合并在一起。如果你多考虑一会儿的话,你会发现合并并不困难。让我们回到扑克牌堆的例子里,以得到更多的细节。由于我们对两个堆都进行了排序,因此每个堆的最小值都在顶部。哪一边顶部的元素更小,那么它就是合并列表里的第一个元素。删除这个更小的值之后,我们可以再次比较两个扑克牌堆顶部的元素,这时,更小的顶部扑克牌将会是列表中的下一个元素。我们一直持续这个过程,把两个顶部元素里较小的那一个放入到最终的大列表里,直至其中一个堆被用完了为止。这个时候,我们用剩余堆里的扑克牌来完成最终的结果列表。

下面是一个归并过程的Python实现。在这段代码里,lst1和lst2是两个小列表,lst3是放置结果的大列表。为了使归并过程能够正常地工作,lst3的长度必须等于lst1和lst2的长度之和。你应该可以通过研究代码里附带的注释来理解这段代码

在这里插入代码片

很好,现在我们可以把列表切成两个小列表,如果这两个列表已经有序了,我们也知道了应该如何把它们合并回一个列表。那么,我们应该怎么对小列表进行排序呢?让我们考虑一下。我们正在尝试对列表进行排序,我们的算法要求我们对两个小列表进行排序。这听起来就像是一个很好的能够使用递归来完成的问题。也许我们使用归并排序(mergeSort)本身,就能够对两个列表都进行排序。让我们回到我们的递归指南来开发一个合适的递归算法。

为了让递归能够正确工作,我们需要找到至少一个不需要递归调用的基本情况。而且,我们还必须要确保递归调用总是在更小版本的原始问题上进行。mergeSort里的递归将会始终出现在大约是原始列表的一半的列表上,因此后面这个属性就自动地满足了。最终,我们的列表将会非常小——只包含一个元素。更好的是,只有一个元素的列表是已经有序了的!这样,我们就有了一个基本情况:当列表的长度小于2的时候,我们什么都不需要去做,保持列表不变就行了。
在这里插入图片描述
同样地,我们可以把这个算法直接转换为Python代码:


def merge(lst1, lst2):  # 有序列表lst1 lst2 合并成lst3
    lst3 = []
    i1, i2 = 0, 0  # 追踪每个列表当前的位置
    n1, n2 = len(lst1), len(lst2)
    while i1 < n1 and i2 < n2:  # lst1 和lst2 都拥有更多元素
        if lst1[i1] < lst2[i2]:  # lst1 的第一个元素更小
            lst3.append(lst1[i1])  # 把这个小的追加到临时列表
            i1 = i1 + 1
        else:  # lst2 的第一个元素更小
            lst3.append(lst2[i2])
            i2 = i2 + 1
    if i1 == n1:
        for i in lst2[i2:]:
            lst3.append(i)
    else:
        for i in lst1[i1:]:
            lst3.append(i)
    return lst3


def mergeSort(nums):
    n = len(nums)
    if n <= 1:
        return nums
    m = n // 2
    left = mergeSort(nums[:m])
    right = mergeSort(nums[m:])
    return merge(left, right)


print(mergeSort([1, 3, 2, 4, 5, 7, 9,2]))


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值