分治
分治的思想&步骤
一、思想
规模比较大的问题分解成较小的问题,较小的问题再解决成更小的问题,直到容易解决为止。
- 对较小的问题的解决,是继续分解——递归方程
- 直到容易解决为止(通常为1个元素)——边界条件
二、步骤
- 分解:将原问题分解成规模较小的子问题,子问题的形式需要和原问题一致;
- 解决:通过递归的方法解决子问题;
- 合并:对子问题的解进行合并,形成原问题的解。
合并排序
- 首先对原数组分解成左右两个子数组(减小问题的规模)(语句4-5)
- 对这两个子数组进行排序,怎么排序?递归调用原函数进行排序即可(语句6-7)
- 最后通过合并操作对排序后的子数组进行合并(语句8)
快速排序
- 分解:选择一个主元素k,将原数组分解成两个子数组,小于主元素的所有元素组成了一个子数组,大于主元素的所有元素组成了另外一个子数组。
- 解决:对子数组进行递归调用解决;
- 合并:通过上述步骤,已经完成对原数组的排序,无需合并操作。
- 复杂度分析:
(计算方法请翻阅上一章递归中所讲述的递归树法和主方法)
最大子数组问题
给定一个数组A,找出此数组所有子数组中和最大的子数组,即为最大子数组问题。
例如:A = {6,-9,7,-2,9,-5}
则:
寻找第k小元素
给定一个无序数组S = [s1,s2,...,sn-1,sn],要求输出这个数字的第k小元素,也就是从小到大排序后的第k个元素。
显然一种简单的方法是:先将数组S排序,再输出第k个元素即可,但这种方法取决于排序的复杂度,而比较排序最优的复杂度为Θ(nlogn),所以这种方法复杂度为Θ(nlogn),有没有方法可以将复杂度降为线性(O(n))?
用分治。
分治基本思路:将原数组分为两个子问题,然后递归解决这两个子问题。但显然,这样做并不能降低算法的复杂度,我们想到二分搜索之所以能够降低复杂度,是因为在递归的过程中,每次都舍弃一个子问题。这里我们也采用相同的策略,将两个子问题舍弃一个,而递归解决剩余的一个子问题。
-
如何将原数组分成两个子问题?寻找一个中间元素。
-
如何找到这个处于中间位置的元素m?
将n个元素划分成为m个组(通常是每组5个元素),取每组的中间元素,再取这些中间元素的中间元素。
-
怎么找到中间元素的中间元素?递归调用。
-
哪个子问题需要舍弃?
将元素分成3组,找到所有小于m、等于m、大于m的元素,找到相应的组,舍弃其他组。
伪代码中的n > 44的依据我们先按下不表。
语句10到语句16,判断第k小元素在哪个子数组,如果在L子数组,则通过递归调用在L子数组寻找第k小元素;如果在R子数组,则通过递归调用在R子数组寻找第k小元素,否则返回m,m即为第k小元素,复杂度为T(max{l,h})
如何估计l和h的值呢?
- 可以换一种方法寻找中项吗?
- 以平均值为中项元素。
显然,改进后的算法更加简洁,但复杂度会有变化。当数据是按比例划分时: