分治算法与快排

分治算法(D&C)


分治算法是一种思想,他把一个大问题缩小为规模比较小的问题,小问题得到解决之后,大问题也随之解决。分治算法通常利用递归来实现。利用递归实现的步骤如下:

  1. 明确递归函数的返回值;
  2. 寻找基线条件,这种条件务必简单;
  3. 实现递归体:不断将问题分解(缩小问题规模),直到符合基线条件;

例如,我们有一块土地,尺寸为 1680 ∗ 640 m 2 1680*640m^2 1680640m2,现要求把这块土地划分为均匀的方块且要求尽可能的大,如何进行划分呢?

在这里插入图片描述

我们利用分治思想来解决这个问题,分治算法的核心在于缩小问题的规模, 1680 ∗ 640 m 2 1680*640m^2 1680640m2的方块可以划分为 640 ∗ 640 m 2 , 640 ∗ 640 m 2 , 640 ∗ 400 m 2 640*640m^2,640*640m^2,640*400m^2 640640m2640640m2640400m2三个方块,其中, 640 ∗ 640 m 2 640*640m^2 640640m2是在现有基础之上能够划分的最大方块。图示如下所示:

在这里插入图片描述

我们的问题现在缩小到在方块 640 ∗ 400 m 2 640*400m^2 640400m2 中寻找最大切分方块。余下的操作以此类推…。那何时停止呢?直到方块缩小到两边长成倍数关系,即可停止。最终的切分图如下所示:

在这里插入图片描述
在这里插入图片描述

经过分治算法的处理可以得到最大的切分方块为 $80*80$。

利用分治算法解决数据累加


假设要用分治算法实现列表: [ 1 , 2 , 3 , 4 ] [1, 2, 3, 4] [1,2,3,4] 的累加和。我们定义一个递归函数,用于计算列表的累加和;首先要明确 D&C 的两个问题:

  1. 基线条件:当列表只有一个元素时,返回元素值;
  2. 递归体:缩小列表规模:
def addlist(arr):
    if len(arr) == 1: # 定义基线条件
        return arr[0]
    elif len(arr) == 0:
        return 0
    else: # 定义递归体
        return arr[0]+addlist(arr[1:])
        
print(addlist([1, 2, 3, 4])) # 10

利用分治算法找出列表中的最大数**


  1. 基线条件:列表元素只有一个;
  2. 递归条件:缩小问题规模,计算剩下列表中最大数;
def calListMax(arr):
    if len(arr) == 0:
        return 0

    if len(arr) == 1: # 基线条件
        return arr[0]

    value = calListMax(arr[1:])
    if arr[0] <= value:
        return value;
    else:
        return arr[0];

print(calListMax([2, 9, 2, 5, 23, 62, 34])) # 62

利用分治算法实现二分查找


  1. 基线条件:找到标签或没有标签;
  2. 递归体:见代码
# 列表不作为参数,可以提高递归的执行效率
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

def binary_sort(lowIndex, hightIndex, value):
    if len(arr) == 0:
        return -1

    if lowIndex > hightIndex:
        return -1
    else:
        mid = (lowIndex+hightIndex)//2;
        if value == arr[mid]:
            return mid;
        elif value < arr[mid]:
            hightIndex = mid-1
        else:
            lowIndex = mid+1

        return binary_sort(lowIndex, hightIndex, value)

print(binary_sort(0, len(arr)-1, 8)) # 7

快速排序


快速排序是一种常用的排序算法,比选择排序快得多。例如, C语言标准库中的函数 qsort 实现的就是快速排序。快速排序也使用了D&C。快速排序的思想流程如下:假设要对列表 [ 33 , 15 , 10 ] [33, 15, 10] [33,15,10] 进行排序,如下所示:

在这里插入图片描述

首先选择一个基准值,比基准值小的数据放在基准值的左边,比基准值大的数据放在基准值的右边,如下所示:

在这里插入图片描述

然后对左区列表和右区列表执行上述相同的操作。其代码如下:

def quick_sort(arr):
    if len(arr) <= 1: # 基线条件
        return arr[0]
    else: # 递归体
        pivot = array[0] # 基准值
        less = [i for i in array[1:] if i <= pivot] # 小于基准值的数列
        greater = [i for i in array[1:] if i > pivot] # 大于基准值的数列
        return quicksort(less) + [pivot] + quicksort(greater)    

快速排序的时间复杂度

快速排序的平均时间复杂度是: O ( n l o g n ) O(nlogn) O(nlogn);在最糟糕情况下时间复杂度为: O ( n 2 ) O(n^2) O(n2)
时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn) 的情况,此时基准值取的列表的中间值:

在这里插入图片描述

当对一个有序数列进行快排且基准值取第一个值时,时间复杂度会是 O ( n 2 ) O(n^2) O(n2),如下图所示:

在这里插入图片描述

递归调用的层数如下:

在这里插入图片描述


总结


  1. D&C将问题缩小为更小规模进行解决。使用D&C处理列表时,基线条件是空数组或单元素数组;
  2. 实现快排时,请随机地选择基准值的元素。快排的平均运行时间为 O ( n l o g n ) O(nlogn) O(nlogn)
  3. 大O表示法中的常量有时候事关重大;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值