分治算法与快排

分治算法(D&C)


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

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

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

在这里插入图片描述

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

在这里插入图片描述

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

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

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

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


假设要用分治算法实现列表:[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] 进行排序,如下所示:

在这里插入图片描述

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

在这里插入图片描述

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

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(nlogn)O(nlogn);在最糟糕情况下时间复杂度为:O(n2)O(n^2)
时间复杂度为 O(nlogn)O(nlogn) 的情况,此时基准值取的列表的中间值:

在这里插入图片描述

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

在这里插入图片描述

递归调用的层数如下:

在这里插入图片描述


总结


  1. D&C将问题缩小为更小规模进行解决。使用D&C处理列表时,基线条件是空数组或单元素数组;
  2. 实现快排时,请随机地选择基准值的元素。快排的平均运行时间为O(nlogn)O(nlogn)
  3. 大O表示法中的常量有时候事关重大;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章