算法入门——分而治之思想之快速排序与归并排序

目录

分而治之

归并排序

python代码实现

时间复杂度 

快速排序

python代码实现

 时间复杂度


 

分而治之

分而治之(divide and conquer,D&C)——一种著名的递归式问题解决方法。

所谓“分而治之” 就是把一个复杂的算法问题按一定的“分解”方法分为等价的规模较小的若干部分,然后逐个解决,分别找出各部分的解,把各部分的解组成整个问题的解,这种朴素的思想来源于人们生活与工作的经验,也完全适合于技术领域。诸如软件的体系结构设计、模块化设计都是分而治之的具体表现。

使用D&C解决问题的过程包括两个步骤:

(1)找出基线条件(终止递归的条件),这种条件尽可能简单。

(2)不断将问题分解(或者说缩小规模),直到符合基线条件

(3)将拆分的子问题的解逐层返回合并成原问题的解

我们以一个例子来说明,我们给定一个数组[1,2,3,4,5,6],我们需要将这些数字相加,并返回他们的和。首先,这个问题可以使用一个for循环就能解决,但是如何用递归函数来完成这个任务呢?

第一步:找出基线条件,就是该何时停止递归了,我认为数组包含一个或0个元素就该终止了

第二步:缩小问题规模,就是每次递归我们都要离基线条件越来越近。

sum([1,2,3,4,5,6])=21似乎与1+sum([2,3,4,5,6])=1+20=21等效的,但是问题的规模,似乎缩小了欸。

第三步:返回每层子问题的解合并成原问题的解。

 python代码实现

def sum(arr):
    """基线条件"""
    if len(arr)<=1:
        return arr[0]
    '''递归条件'''
    return arr.pop()+sum(arr)
if __name__ == '__main__':
    arr=[1,2,3,4,5,6]
    print(sum(arr))

这只是举个例子来说明D&C这种思想,但这个求和的算法效率其实并不比循环的效率高,甚至肯会更糟。

归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用D&C的一个非常典型的应用。

合并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。归并排序也称合并排序。

关于归并排序的图解,大家可以去阅读这篇文章图解排序算法(四)之归并排序 - dreamcatcher-cx - 博客园

python代码实现

"""归并"""
def Merge_sort(arr):
    if len(arr)<=1:
        return arr
    mid=len(arr)//2
    """递归划分成两个数组"""
    left=Merge_sort(arr[:mid])
    right=Merge_sort(arr[mid:])
    return Sort(left,right)

"""排序"""
def Sort(a,b):
    c=[]
    while len(a)>0 and len(b)>0:
        """如果a的第一个元素比b的第一个元素大,就将b的第一个元素弹出并添加到数组c中,
        反之就将a的第一个元素弹出并添加到c中,当a或者b中任意一个数组为空时,跳出循环"""
        if a[0]>b[0]:
            c.append(b.pop(0))
        else:
            c.append(a.pop(0))
    """如果a数组为空,就将b数组添加到c中,反之相同"""
    if len(a)==0:
        c.extend(b)
    else:
        c.extend(a)
    return c
if __name__ == '__main__':
    a=[2,6,5,20,1,4,22]
    print(Merge_sort(a))

 测试结果:

[1, 2, 4, 5, 6, 20, 22]

时间复杂度 

归并排序的时间复杂度为O(n*logn),最平均情况和最糟情况均为O(n*logn),因为它的归并时间复杂度为O(logn),排序的时间复杂度为O(n),故总的时间复杂度为O(nlogn)。

快速排序

快速排序是一种常用的排序算法,比选择排序快许多,是冒泡排序算法的优化,也是D&C思想的体现。

快速排序算法通过多次比较和交换来实现排序,其排序步骤如下:

(1)选取基准值(pivot

(2)根据基准值将数组分成两个子数组:小于基准值和大于基准值的数组

(3)对这个两个子数组重复上述操作,直至满足基准条件

我们以排序数组[2,0,5,3,1,6]为例,它的步骤如下所示

python代码实现

def Quick_sort(arr):
    """基线条件"""
    if len(arr)<1:
        return arr
    pivot=arr[0]#选取基准值
    left=Quick_sort([i for i in arr[1:] if i<pivot])#小于基准值
    right=Quick_sort([i for i in arr[1:] if i>=pivot])#大于基准值
    result=left+[pivot]+right
    return result
if __name__ == '__main__':
    a=[2,0,5,3,1,6]
    print(Quick_sort(a))

 测试结果:

[0, 1, 2, 3, 5, 6]

 时间复杂度

快速排序的性能依赖于你选择的基准值,平均情况(最优情况)下的时间复杂度为O(nlogn),最糟情况下的时间复杂度为O(n^2)。假如每次我们选择数组第一个元素为基准值,且要排序的数组是有序的,那么其递归次数为n,即调用栈的高度为n,栈长为O(n),调用栈每层都涉及O(n)个元素,所以总的时间复杂度为为O(n^2);假如每次我们选择数组中间值为基准值,那么其递归次数为log_2{n},栈长为O(log_2{n}),调用栈每层也涉及O(n)个元素,所以总的时间复杂度为O(nlogn)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值