![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=1739378686-xOneQEO2kzS9vGHwHTi3utN4oM74Vkv4-0-c4b5733c0f92e424e03bcffc35a7e3a2)
图3-1 分治法思想
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/32_03.jpg?sign=1739378686-nfqa44s6DjW0AIMDuiczQnAlfkp3uFui-0-90d2e418f55e70cea1157a8da0e2fa34)
在用分治法设计算法时,最好使子问题的规模大致相同,即将一个问题分成大小相等的k个子问题(许多问题取k=2)。这种使子问题规模大致相等的做法出自一种平衡子问题的思想,通常比子问题规模不等的做法要好。
例3.3 分治法求数列中最大最小值。
问题描述:输入一个数组A[1,…, n],求出A中的最大值与最小值。
方法思想:使用分治法的思想,首先把数组分成两部分,再把这两部分中的每一部分再分成两部分,一直递归分解下去直到每一部分小于或等于两个数为止,然后比较这两个数大小,然后回弹比较直到递归的最外层,就可以找到数组中的最大最小值,代码如下所示。
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/33_01.jpg?sign=1739378686-dNS0kYnsmju55Ev1BZgthnMN6EzA9rRK-0-88fa38a74debae02db76330585f0498a)
伪代码已经给出了算法的逻辑,只需要根据Python语言的特性转换成Python的实现即可,实现代码如下:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/33_02.jpg?sign=1739378686-IDJSaA8fVdZIcGC58k7ZAD0SpcZghX9Q-0-09d8b8be53c7fb85cb72874c733e228d)
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_01.jpg?sign=1739378686-MDMxmyFQquR4kUhYV4X6UCboF2C2SXr9-0-c0a6e94e3aac6b1215d18425584d8705)
程序运行结果为:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_02.jpg?sign=1739378686-MOEtBWRRnkNUeU2hHZFOfBtGwV1D2xkB-0-8f947b7f1e2dc200ad0d9a317d6b7725)
现在来分析上述代码中算法的时间复杂度,用n表示待查找数组中元素的个数,用T(n)表示该问题的时间复杂度。根据算法中的递归关系可以得出:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_03.jpg?sign=1739378686-6EMkepVFygpW1w8V4EEQW3e4JhnaBew0-0-737e646909ab131a5776a8d0f04cff4b)
根据上一章讲述的算法分析方法,可以得到。
总的来说,分治法所能解决的问题一般具有以下几个特征:
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=1739378686-ADFJKNY9OLy0r7YF33BUu1Ej3ajEd7uh-0-bcccb00e8a2a3f24bd88a1f3d2dc9200)
通过迭代法求得方程解为:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/34_06.jpg?sign=1739378686-ppRu2bZ2PawdhU8nfObBkc1MJ0WZwHVj-0-235393c83d9f14ab60f4c5820c155ef6)
其中,nlogmk为基本子问题所消耗的时间,则为合并部分所消耗的时间。设f(n)=Θ(nd), d≥0。依照主定理可得:
![](https://epubservercos.yuewen.com/F06DB7/21570844101311606/epubprivate/OEBPS/Images/35_01.jpg?sign=1739378686-J0ovCEySq5P75u2U2vfyyDueKuUxuxcy-0-b8de32328dc023c6003884286e0045fc)