步骤
分治法的基本步骤是:分解、求解子问题、合并。
下面以快速排序、和归并排序作为正面例子进行说明。
然后以求解最大连续子序列和问题作为反面例子说明——当子问题不独立的时候使用分治法不是最有效的。
快速排序
- 分解: 选取基准元素,将原序列a[s…t]分解成两个子序列a[s…i-1]和a[i+1…t], 其中a[i]是基准元素的位置。
- 求解子问题:若子序列的长度为0或1直接返回,否则递归地解决子问题
- 合并:由于排序过程是就地进行的,所以合并不需要任何操作
可以看出,快速排序的主要操作就是分解,而且这个分解不是像切东西一样去直接把原序列"切成子序列",而是根据需要对原序列先"洗牌", 再切。
归并排序
循环 ⌈ l o g 2 n ⌉ \lceil log_2n\rceil ⌈log2n⌉次,length依此取 1, 2, …, l o g 2 n log_2n log2n。
- 分解:将原序列分解成长为length的若干序列
- 求解子问题:由于每轮处理的时候,子序列已经有序了,所以不需要额外处理
- 合并:采用归并算法将相邻序列进行合并。
可以看出,归并排序的主要操作就是合并
最长连续子序列和
对于含有n个整数的序列a[0…n-1],若n=1,表示该序列仅含一个元素,如果该元素大于0,则返回该元素;否则返回0。
若n>1,采用分治法求解最大连续子序列时,取其中间位置mid=(n-1)/2,该子序列只可能出现3个地方。
- 该子序列完全落在左半部即a[0…mid]中。采用递归求出其最大连续子序列和maxLeftSum。
- 该子序列完全落在右半部即a[mid+1…n-1]中。采用递归求出其最大连续子序列和maxRightSum。
- 该子序列跨越序列a的中部而占据左右两部分
计算maxLeftBorderSum和maxRightBorderSum需要O(n)的时间复杂度。可以计算出总的时间复杂度是O(nlog2n)。而采用贪心法的时间复杂度是O(n)
原理
分治法所能解决的问题一般具有以下几个特征:
- 1)该问题的规模缩小到一定的程度就可以容易地解决
- 2)该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
- 3) 利用该问题分解出的子问题的解可以合并为该问题的解;
- 4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、
第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。