利用Python实现归并排序

在讲归并排序之前我们先来了解一下什么是分治算法。为什么归并排序属于分治算法的体现。

分治算法

分治算法基本思想就是将一个比较大规模的问题分解成为若干个规模较小的性质和原问题性质必须要保持一致。

分治算法特征

1.  该问题可以分解成为程序能够执行的子问题。
2.  该问题能够分解。意思就是问题具有最优子结构。
这里我们可以理解成问题通过分解能够递归的来实现解。
3.  该问题分解出来的子集问题得到的解能够合并成为该问题的解。
这个非常重要,直接决定这个问题能否使用分治算法,同时满足这个条件意味着子集问题与原问题在结构上具有一致性。
4.  该问题分解出来的子集问题得出来的解是相互独立的,意味着子集解中没有交集。
这个直接影响分治算法的效率,虽然我们也可以专门对交集做处理,但这样会浪费大量时间。

归并排序

是建立在归并操作上的一种排序算法,该方法基本思想采用了分治法来解决。
将已经有序的两个序列集合合并来得到解,首先我们让每个子集序列集有序化,再让每个子集序列间段有序化然后合并得到解。

归并算法思路

1.申请三段空间,其中两段空间用于存储两个序列集,另外一个储存排序之后我们得到的结果,称之为合并序列。
2.设定两个指针,这两个指针分别指向两个待比较的序列子集的初始位置。
3.比较两个指针所指向的元素值大小,将小的序列中的元素存入合并序列
中,然后移动合并序列和已经取出的元素的序列指针指向下一位。
4.重复步骤三做比较,直到一个序列的指针指到队尾。
5.将另外一个比较序列剩下的所有元素追加到合并序列的队尾

归并排序的直观排序图
这里写图片描述

代码实现

在贴代码段之前对于初学者来说到底该怎么写代码我觉得有必要说一下。代码真的不是一气呵成的,而且也不是想当然写出来的。
这个时候可能需要反复断点中断来查看是否有逻辑错误。在这里建议大家包括我在内,在理解了问题的基础下我们需要先把大体的代码框架最好先写出来,特别是主要的逻辑判断语句。
但是不需要太care我循环体或者判断里面语句怎么实现,当你把这一步做到的时候能避免很多不必要的错误发生。在尝试自己写归并排序的时候我也犯了刚刚的错误,下面贴上代码。
import random

def ConfiationAlgorithm(str):
    if len(str) <= 1: #子序列
        return str
    mid = (len(str) / 2)
    left = ConfiationAlgorithm(str[:mid])#递归的切片操作
    right = ConfiationAlgorithm(str[mid:len(str)]) 
    result = []
    #i,j = 0,0

    while len(left) > 0 and len(right) > 0:
        if (left[0] <= right[0]):
            #result.append(left[0])
            result.append(left.pop(0))
            #i+= 1
        else:
            #result.append(right[0])
            result.append(right.pop(0))
            #j+= 1

    if (len(left) > 0):
        result.extend(ConfiationAlgorithm(left))
    else:
        result.extend(ConfiationAlgorithm(right))
    return result   
if __name__ == '__main__':
    a = [20,30,64,16,8,0,99,24,75,100,69]
    print ConfiationAlgorithm(a)
    b = [random.randint(1,1000) for i in range(10)]
    print ConfiationAlgorithm(b)
这里我打算是想用一个方法来实现归并排序,这个方法里面包含了三个部分:
第一个部分切片操作,
第二个部分比较操作,
第三个操作针对子序列多出来的数将追加到合并序列中。
但是一开始在第二部分比较的时候犯了很严重的错误就是一开始的时候条件没有写明白,导致我在循环体重填充代码的时候出现了各种各样的错误,这种情况下只能断点中断来排查。
注释的语句段就是我直接对切片的子序列做操作,然后没有弹出比较完的数,导致第一遍循环直接死循环然后报错。其实设i,j来比较子序列的数可以不可以,答案当然是可以的,当时问题就在于我这是一个方法。
要按照注释里面来写的话其实我的循环条件是有问题的。在代码片中首先用left来切片包含两个元素的子序列出来然后left,right最初都只有一个数通过比较之后完成了子序列的有序性。
然后我觉得非常有意思的就是两个return的作用。如果不明白的童鞋可以断点中断来看看,分析分析。

时间复杂度

归并排序是分治算法的一种提现,按照分治算思想的童鞋来说可以很容易理解。
总时间=分解时间+解决问题时间+合并问题时间。在这里分解问题的时间为一个常数,就是把一个待排序的序列分解成两个序列,时间复杂度为O(1),解决问题的时间就是将一个规模为N的问题来递归成两个规模为N/2的子问题,时间复杂度为2T(N/2)。
合并的时间复杂度只取决于你的深度O(N)。总时间T(N)=2T(N/2)+O(N)。在这里我们略去繁琐的数学证明来简要的说一下。

这里写图片描述

把图中所有的项加起来就是总的执行时间。这其实就是一颗完全二叉树,每一层的和都是Cn,一共有log(n)+1层,因此总的执行时间Cnlog(n)+Cn,可得出时间复杂度为O(nlogn)。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值