算法图解第四章笔记与习题(快速排序)
文章目录
4.1 分而治之
分而治之(divide and conquer)D&G是一种著名的递归式问题解决方法。——分而治之是递归的。
分而治之的算法是递归的。使用分而治之算法解决问题的过程包括两个步骤。
(1)找出基线条件,这种条件必须尽可能接单。
(2)不断地将问题分解(或者说缩小规模),直到使其符合基线条件。
例如,对于数组类问题的基线条件,则常常是数组为空,或数组内仅有一个元素。分解方式则通常是将其分为两个尽可能等大小的数组。
4.2 快速排序
def quicksort(array):
if len(array) < 2: # 基线条件:为空或只包含一个元素的数组是“有序”的
return array
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) # 将子数组进行快速排序,再与基准值一同组成有序数组。
print(quicksort([10, 5, 2, 3]))
4.3 大 表示法的平均情况和最糟情况
-
最糟情况指的是算法运行时可能遇到的最坏的情况。
-
平均情况指的是算法遇到时的最佳情况。(最佳情况也是平均情况)
快速排序的平均情况为 ,最糟情况为 。这取决于基准值选取的好坏。
当每次基准值选取的均为最差情况时,则需要对包含 个元素的数组进行 次基准值的选取。当每次基准这选取的均为最佳情况时,则仅需要 次选取。(二分)
而当每一次选取基准值后,数组的每个元素均需要和基准值进行对比,因此,每次选取基准值之后需要进行 次比较。
因此,快速排序的最糟情况为 ,平均情况为 。
4.4 小结
- 分而治之算法将问题逐步分解。使用分而治之算法处理列表时,基线条件很可能是空数组或只包含一个元素的数组。
- 实现快速排序时,请随机地选择用作基准值的元素。快速排序的平均运行时间为 。
- 大 表示法中的常量有时候事关重大,这就是快速排序比合并排序快的原因所在。
- 比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时, 的速度比 快得多。
练习
习题4.1
- 请编写前述sum函数的代码。(递归求和,基线条件为数组空)
def sum(arr):
if arr == []: # 基线条件:数组为空
return 0
# elif len(arr) == 1: # 基线条件:数组内只有一个元素(可省略)
# return arr[0]
else:
return arr[0] + sum(arr[1:]) # 递归条件:计算数组中第一个元素外元素的和,并与第一个元素再相加
习题4.2
- 编写一个递归函数来计算列表包含的元素数。
def count(arr):
if arr == []: # 基线条件:数组为空
return 0
# elif len(arr) == 1: # 基线条件:数组内只有一个元素(可省略)
# return 1
else:
return 1 + count(arr[1:]) # 递归条件:计算数组中第一个元素外剩余元素个数,再加一
上面两题中,数组内只有一个元素的情况均可省略。因为数组只有一个元素——即剩余数组为空的情况,已经被基线条件包含了,因此可以省略。
习题4.3
- 找出列表中最大的数字。
def max(arr):
if arr == []: # 基线条件:数组为空
return None
elif len(arr) == 1: # 基线条件:数组内只有一个元素
return arr[0]
elif len(arr) == 2: # 基线条件:数组内有两个元素,返回最大值(可用内置的max()代替)
if arr[0] > arr[1]:
return arr[0]
else:
return arr[1]
else:
return max([arr[0], max(arr[1:])]) # 递归条件:比较第一个元素与数组中剩余元素的最大值。
习题4.4
- 还记得第1章介绍的二分查找吗?它也是一种分而治之算法。你能 找出二分查找算法的基线条件和递归条件吗?
基线条件:数组中仅剩一个元素(是否为查找元素为另外的判断,不属于基线条件之内)
递归条件:将数组分为两部分,如果查找值比第一部分的最大值小,则对第一部分继续二分查找,否则对剩余部分进行二分查找。
使用大O表示法时,下面各种操作都需要多长时间?
习题4.5
- 打印数组中每个元素的值。
每个元素进行一次打印操作。$O(n) $。
习题4.6
- 将数组中每个元素的值都乘以2。
n个元素进行一次乘法操作。故为 。
习题4.7
- 只将数组中第一个元素的值乘以2。
。
习题4.8
- 根据数组包含的元素创建一个乘法表,即如果数组为[2, 3, 7, 8, 10],首先将每个元素 都乘以2,再将每个元素都乘以3,然后将每个元素都乘以7,以此类推。
n个元素做n次乘法操作。故为 。