算法与数据结构| 深入理解分而治之与快速排序

##一.分而治之
分而治之(divide and conquer,D&C)是一种解决问题的重要的策略。它不仅是一种递归式的解决方法,更是一种分析问题的思维模式,尤其在程序设计中反映着一个人的算法内功,其重要性不言而喻。

下面我们先来假设这样一个场景:
你要给自己的卧室铺瓷砖,卧室的地面空间是一个矩形,瓷砖是正方形,要求你将尽可能大的瓷砖均匀的铺在地面上,你会怎么做?

这个问题有三个核心的关键点,首先要是正方形,然后要尽可能大,最后要均匀分布

那么请你拿出一张纸,画出一个矩形,然后尝试着划分一下,如果你划分出来的方块其中有长方形,或者你划分的密密麻麻,或者你划分并不均匀(即里面的正方形有大小不一致的),出现这三种情况的任意一种,那很遗憾,这是不符合要求的!

这时我们就应该采取分而治之的策略!因为分而治之是递归的,因此我们需要解决两个步骤:

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

这里我们首先要介绍一种新的算法,即欧几里得算法(也叫辗转相除法),如果你还记得起数学归纳法你会很快理解它。

这里写图片描述
两个整数的最大公约数是能够同时整除它们的最大的正整数。辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的差的最大公约数。例如,252和105的最大公约数是21( 252 = 21 × 12 ; 105 = 21 × 5 );因为 252 − 105 = 21 × (12 − 5) = 147 ,所以147和105的最大公约数也是21。在这个过程中,较大的数缩小了,所以继续进行同样的计算可以不断缩小这两个数直至其中一个变成零。这时,所剩下的还没有变成零的数就是两数的最大公约数。由辗转相除法也可以推出,两数的最大公约数可以用两数的整数倍相加来表示,如 21 = 5 × 105 + (−2) × 252 。这个重要的结论叫做贝祖定理。

这种算法的思想其实类似于多米诺骨牌效应(数学归纳法也采用这种思想),即:
1.证明第一张骨牌会倒。
2.证明只要任意一张骨牌倒了,那么与其相邻的下一张骨牌也会倒。
骨牌一个接一个倒下就如同一个值接下一个值
那么便可以下结论:所有的骨牌都会倒下。
注意:虽然数学归纳法名字中有“归纳”,但是数学归纳法并非不严谨的归纳推理法,它属于完全严谨的演绎推理法。事实上,所有数学证明都是演绎法。

有了这种算法思想,我们再来解决铺瓷砖的这个问题就会变的异常轻松。
将结论中转换为这道题,就会变成适用于这小块地的最大方块,也是适用于整块地的最大方块。

举个例子:
首先最容易处理的情况是,一边的长度是另一边的整数倍。
比如一长50,宽25的矩形你就会轻易的分出两个25X25的正方形。
而如果是一个长168,宽64的矩形,你应该这样分:先从这块矩形中划分出两个64X64的正方形(满足当前要求的最大正方形),那么就会产生一个64X40的新矩形,在该矩形中你采取同样的方式划分出一个40X40的正方形(满足当前要求的最大正方形),那么就又会产生一个24X40的矩形,再划分出一个24X24的正方形,产生一个24X16的矩形,再划分出一个16X16的正方形,产生一个16X8的矩形,最后划分出两个8X8的正方形,至此,你该片土地上,使用的最大瓷砖便为8X8的正方形!

这里我们重申一下D&C的工作原理:
1.找出简单的基线条件
2.确定如何缩小问题的规模,使其符合基线条件。

##二.快速排序
接下来,我们介绍一种常用的排序算法,比选择排序要快的多。例如:C语言中的qsort实现的就是快速排序,这种算法就使用了D&C。

当我们对一个数组中的元素进行排序时,如果我们采用递归,那么基线条件就应该是当数组为空或者只包含一个元素时(即数组为空或为一个值时不需要排序)。

def quicksort(array):
	if len(array)<2:
		return array

下面介绍快速排序的工作原理:
当一个数组中有多个数据元素时,例如:18,53,21,88,2,100,72利用D&C,你需要将这个数组进行分解直到它满足基线条件为止,首先你需要从中选择一个元素,这个元素被称为基准值(pivot),例如我们选择53(随意选取),接下来找出比基准值小的元素以及比基准值大的元素,并将小的放在基准值的左边,大的放在基准值的右边,此时即为18,21,2,53,88,72,100.这个过程称为分区(partitioning),这里只是进行了分区,得到的左右两个数组仍然是无序的。如果我们能以同样的方式对子数组排序,最后合并结果就可以得到一个有序数组!

即我们对左子数组18,21,2,例如选取18作为基准值,小左大右,即变为2,18,21
即我们队右子数字88,72,100,例如选取88作为基准值,小左大右,即变为72,88,100

以此类推。。。

quicksort([18,21,2]) + [53] + quicksort([88,72,100])
>[2,18,21,53,72,88,100]

所以快排序的步骤如下:
1)选择基准值
2)将数组拆分为两个子数组:小于基准值的元素和大于基准值的元素.
3) 对这两个子数组进行快速排序。

##三.源代码(python)

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])

猜你喜欢

转载自blog.csdn.net/qq_38497820/article/details/80992655