快速排序
1.分而治之
分而治之(divide and conquer, D&G)——一种著名的递归式问题解决方式
D&G,通过三个实例来介绍。
假设你是农场主,有一小块土地
你要将这块地均匀分成方块,且分出的方块要尽可能的大。
如何将一块地均匀分成方块,并确保分出的方块是最大的呢?使用D&C策略。D&C算法是递归的。使用D&C解决问题的过程包括两个步骤。
(1)找出基线条件,这种条件必须尽可能简单
(2)不断将问题分解,直到符合基线条件。
首先,找出基线条件。最容易处理的情况就是,一条边的长度是另一条边的整数倍。
如果一边长25m,另一边长50m,那么可使用25x25.
然后,找出递归条件,这就用到D&C,根据D&C的定义,每次递归调用都必须缩小问题的规模。如何缩小问题的规模呢?我们首先找出这块地的可容纳的最大方块。
根据欧几里得算法可证——适用于小地块的最大方块,也适用于整块地的最大方块。
所以我们只需要不停地分割最小的方块,直到余下的土地满足基线条件。
再看一个例子,给定一个数字数组[2,4,6]。
你需要建这些数字相加,并返回结果。使用循环很容易完成这种任务。
def sum(arr): total = 0 for x in arr: total +=x return total print sum([1,2,3,4])
但是如何使用递归函数完成这种任务呢?
第一步,找出基线条件。最简单的数组就是只包含一个元素或者不包含
第二步,每次递归调用都必须离空数组更近一步。
def sum(arr):
if arr == []:
return 0
else:
return arr[0]+sum(arr[1::])
print sum([1,2,3,4])
2.快速排序
快速排序是一种常用的排序算法,比选择排序快得多。
下面使用快速排序对数组进行排序。对排序算法来说,最简单的数组是什么样的呢?就是不需要排序的数组——空数组或只有一个元素的数组。
因此基线条件为数组为空或只包含一个元素。在这种情况下,只需原样返回数组——根本不用排序。
def quicksort(array): if len(array) < 2: return array
对于包含多个元素的数组,要使用D&C,需要将数组进行分解,直到满足基线条件。
首先,从数组中选取一个元素,这个元素被称为基准值。(稍后说明如何选取合适的基准值,暂定第一个元素)
接下来,找出比基准值小的元素以及比基准值大的元素,这被称为分区。现在我们拥有:
- 一个由所有小于基准值的数字组成的子数组;
- 基准值
- 一个由所有大于基准值的数组组成的子数组
在这里只是进行了分区,得到的两个数组是无序的。但是如果两个数组是有序的,对整个数组进行排序将非常容易。
如何对子数组进行排序呢?只需要对这两个子数组在进行快速排序,再合并结果,就能得到一个有序的数组。
def quicksort(array): if len(array) < 2: return array else: pivot = array[0] less = [i for i in array[i:] if i <= pivot] greater= [i for i in array[i:] if i <= pivot] return quicksort(less) + [pivot] + quicksort(greater) print quicksort([10,5,3,4])