![Python算法从菜鸟到达人](https://wfqqreader-1252317822.image.myqcloud.com/cover/713/41398713/b_41398713.jpg)
3.2 分治法
分治(Divide-and-Conquer)就是“分而治之”的意思,分治法的基本思想就是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相关联。通过递归方式来求解这些子问题,然后将各个子问题的解合并成原问题的解,如图3-1所示。它的一般的算法设计模式代码如下所示。
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/32_02.jpg?sign=1738964271-4wYaUsi6NvnHzPxVjoF4xsb7c3VC9Qxp-0-a41f051aa3a612368fe762577228dad4)
图3-1 分治法思想
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/32_03.jpg?sign=1738964271-usVkeVBZL1GqJ4onUZwILbQWd4c3pniD-0-43479d5ae4d62a0aa2b0c1fd47041ba1)
在用分治法设计算法时,最好使子问题的规模大致相同,即将一个问题分成大小相等的k个子问题(许多问题取k=2)。这种使子问题规模大致相等的做法出自一种平衡子问题的思想,通常比子问题规模不等的做法要好。
例3.3 分治法求数列中最大最小值。
问题描述:输入一个数组A[1,…, n],求出A中的最大值与最小值。
方法思想:使用分治法的思想,首先把数组分成两部分,再把这两部分中的每一部分再分成两部分,一直递归分解下去直到每一部分小于或等于两个数为止,然后比较这两个数大小,然后回弹比较直到递归的最外层,就可以找到数组中的最大最小值,代码如下所示。
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/33_01.jpg?sign=1738964271-udwuknfviFvTxG5nfGqVcBUXOWagRcPX-0-5a39237eda27b6f7190280539c078650)
伪代码已经给出了算法的逻辑,只需要根据Python语言的特性转换成Python的实现即可,实现代码如下:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/33_02.jpg?sign=1738964271-gHWuhQkmVhK2EAU8Zg4WHTSvrJeh1psT-0-1b9977c1f99756d772bf0500dcd086f6)
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_01.jpg?sign=1738964271-042xlduy9V1a2kq0GfputuM1rxs19sAL-0-7c1aec8554b93f9742f441b8429204eb)
程序运行结果为:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_02.jpg?sign=1738964271-B7uzRjouFm9IlSAOBB67ur9obAicxAEt-0-6105b9dd830cdbbfd51c4feaa7e765f7)
现在来分析上述代码中算法的时间复杂度,用n表示待查找数组中元素的个数,用T(n)表示该问题的时间复杂度。根据算法中的递归关系可以得出:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_03.jpg?sign=1738964271-jl2X0XA5LNJGqo5l7RS8S7DWFgBx1sup-0-2d3ab144fd909152f469628d81a3c14f)
根据上一章讲述的算法分析方法,可以得到。
总的来说,分治法所能解决的问题一般具有以下几个特征:
1)可行性。该问题的规模缩小到一定的程度时就可以容易地解决;因为问题的计算复杂性一般是随着问题规模的增加而增加,因此大部分问题满足这个特征。
2)可分解性。该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质,这条特征是应用分治法的前提,它也是大多数问题可以满足的,此特征反映了递归思想的应用。
3)可合并性。利用该问题分解出的子问题的解可以合并为该问题的解;能否利用分治法完全取决于问题是否具有这条特征,如果具备了前两条特征,而不具备第三条特征,则可以考虑贪心算法或动态规划。
4)独立性。该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。这条特征涉及分治法的效率,如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然也可用分治法,但用动态规划或贪心法会更好。
下面重点来分析分治法的复杂性,从一般设计模式看,用分治法设计的程序通常是一个递归算法。若一个分治法将规模为n的问题分成k个规模为n/m的子问题去解。假设规模为1的问题耗费1个单位时间,再设将原问题分解为k个子问题以及将k个子问题的解合并为原问题的解需用f(n)个单位时间。用T(n)表示该分治法求解规模为|P|=n的问题所需的计算时间,则有:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_05.jpg?sign=1738964271-BJj7jzz2rFxTVBtLAAh51PQs08oPhBE6-0-b7f2b69d88da633612352f464a941c7f)
通过迭代法求得方程解为:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_06.jpg?sign=1738964271-LM9kPjPoKTf9gt637e2fwO6621aRTTXg-0-8ce6a47ab2ee644f75d943ffe5929691)
其中,nlogmk为基本子问题所消耗的时间,则为合并部分所消耗的时间。设f(n)=Θ(nd), d≥0。依照主定理可得:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/35_01.jpg?sign=1738964271-dejldy2XD9cppbpaX2yvq5s7guUy3UVu-0-f4288d2f202dd5b4e44273507184ae8f)