4快速排序

内容

  • 学习分而治之。
  • 学习快速排序。

1.分而治之

D&C(devide and conquer):一种著名的递归式问题解决方法。

1.1 农场主土地

假设农场主有一块土地(长1680米,宽640米),要将这块地均匀的分成方块,且分出的方块尽可能大。

欧几里得算法:适用于这块地的最大方块,也是适用于整块地的最大方块。
使用D&C解决问题的过程包括两个步骤:

  • 1.找出基线条件,这种条件必须尽可能简单
  • 2.不断将问题分解(或者说缩小规模),直到符合基线条件

按照这个思路

  • 1.先寻找基线条件:方块长度等于宽度。
  • 2.分解问题:每次先去除大的方块,针对剩下的区域进行递归,直到最后得到正方形。
    具体步骤:
    1680*640去掉2个边长640的正方形,得到640*400;
    640*400去掉边长400的正方形,得到40*240;
    400*240去掉边长240的正方形,得到240*160;
    240*160去掉160的正方形,得到160*80;
    160*80可以划分为2个边长80的正方形,符合基线条件,终止递归。

1.2 数组求和

D&C不是解决问题的算法,而是一种解决问题的思路。
给定一个数字数组[1, 2, 3, 4, 5, 6, 7, 8, 9],进行求和。
方法一:使用循环很容易完成这种任务。

def sum(arr):
    total = 0
    for x in arr:
        total += x
    return total

方法二:使用递归函数来完成任务
第一步:找出基线条件
最简单的数组是空数组,其和是0
其次是包含1个元素的数组,其和是这个元素的值。
第二部:每次递归调用都必须离空数组更近一步。
这时只获取第一个数组的值,剩下的数组构成一个新的数组进行分解运算

def sum(arr):
    total=0
    if(len(arr))==0:
        total+=0
    else:
        total+=arr[0]
        total+=sum(arr[1:])
    return total
list=[1,2,3,4,5,6,7,8,9]
print(sum(list))    #45

1.3 练习

1.3.1编写一个递归函数来计算列表包含的元素数。
没有使用len(arr),否则没啥意义了。

def sum(arr):
    total=0
    try:
        arr[0]
        total += 1
        total += sum(arr[1:])

    except IndexError as e:
        print("到达基线条件:数组为空")
    finally:
        return total
list=[1,2,3,4,5,6,7,8,9]
print(sum(list))

1.3.2找出列表中最大的数字

def getMax(arr):
    #数组长度为0,直接返回None
    value=None
    if (len(arr)>0):
        value=arr[0]
        value=digui(arr[1:],value)
    return value
def digui(arr,value):
    #基线条件,数组长度为1;否则就可以持续分割进行递归
    value = value if value > arr[0] else arr[0]
    if(len(arr)>1):
        #数组长度大于1,继续对比
        value=digui(arr[1:],value)
    return value

print("结果:",getMax([1,2,3,4,5,6,7,8,9,15]))
print("结果:",getMax([9,8,7,6,5,4,3,2,1]))
print("结果:",getMax([]))

2.1 快速排序

基线条件:最简单的数组是包含0个元素或1个元素的数组,不需要排序
递归条件:对于长度超过2的数组,使用D&C分解数组,直到满足基线条件。
首先,从数组中选择一个元素作为基准值。
然后分区,即找出比基准值大的元素和比基准值小的元素。然后会得到小于基准值的子数组、基准值、大于基准值的子数组。
递归,对长度大于2的子数组重复操作。

def quickSort(arr):
    if(len(arr)<2):
        return arr
    else:
        pivot = arr[0]
        less = [ i for i in arr[1:] if i<= pivot ]
        greater = [ i for i in arr[1:] if i>=pivot ]
        return quickSort(less)+[pivot]+quickSort(greater)
print(quickSort([10,5,2,3]))

3再谈大O表示法

快速排序的独特指出在于,其速度取决于选择的基准值。

假设每秒执行10次

数组长度二分查找简单查找快速查找选择排序旅行商问题算法
100.3秒 1秒 3.3秒10秒 4.2天
1000.6秒10秒66.4秒16.6分2.9*10149
10001秒100秒996秒27.7小时1.27*102559

3.1 算法中的常量

运行时间=c计算次数,c是算法所需的固定时间量。
如:简单查找每次用时10毫秒,二分查找每次用时1秒。
简单查找=c
n,
二分查找=c*log2n。
假设在40亿个元素的列表中查找,简单查找用时463天,二分查找用时32秒。
这是常量的影响取决于数组的长度。平均情况下,二分查找快得多。因为相对于最糟情况,遇上平均情况的可能性要大得多。

3.2 平均情况和最糟情况

快速排序的性能高度依赖于你选择的基准值。
假设有一个包含8个元素的数组[1, 2, 3, 4, 5, 6, 7, 8]
最糟情况:每次使用第一个元素作为基准值。数组并没有被分为两半,其中一个子数组一直为空。调用栈的长度为n,用时O(n)。
最佳情况:每次去中间的元素作为基准值。这样调用栈将短得多。调用栈的长度为O(log2n)。

原文地址:https://www.cnblogs.com/csj2018/p/12073397.html