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