(翻译文)
本文介绍分治算法的工作原理,同时,将分治算法与其他解决递归问题的方法进行比较。
分治算法是一种解决大问题的策略,解决思路如下:
1.将问题分解为较小的子问题;
2.解决子问题,然后
3.合并它们以获得所需的输出。
要使用分治法,需要使用递归。
1. 分治算法如何工作?
涉及的步骤如下:
1.分解:使用递归将给定问题分为子问题;
2.处理:递归求解较小的子问题。如果子问题足够小,则直接求解。
3.组合:合并子问题的解决方案,这是递归过程的一部分,得到实际问题的解。
让我们借助示例来理解这个概念。
在这里,我们将使用分治的方法对数组进行排序(即合并排序,merge sort)。
1.待排序的数组如下:
2.将数组分为两部分。
同样,将每个部分递归地分为两半,直到获得单个元素。
3.现在,以排序的方式组合各个元素。在这里,划分和合并的步骤并存。
2. 计算复杂度
分治法的复杂度是用主定理计算的。
T(n) = aT(n/b) + f(n),
在这里,
n = 输入大小
a = 递归中子问题的数量
n/b = 每个子问题的大小。假定所有子问题都具有相同的大小。
f(n) = 在递归调用之外完成的工作的成本,包括划分问题的成本和合并解决方案的成本
让我们举一个例子来了解递归问题的时间复杂度。
对于合并排序,等式可以写成:
T(n) = aT(n/b) + f(n)
= 2T(n/2) + O(n)
这里,
a = 2 (每次将一个问题分为2个子问题)
n/b = n/2 (每个子问题的大小是输入的一半)
f(n) = 划分问题和合并子问题所用的时间
T(n/2) = O(n log n) (要理解这一点,请参考主定理。)
现在, T(n) = 2T(n log n) + O(n)
≈ O(n log n)
3. 分治vs动态方法
分而治之的方法将问题分为较小的子问题,这些子问题可以递归进一步解决。每个子问题的结果都不存储,而在动态方法中,每个子问题的结果均存储,以供将来参考。
当多次尝试解决同一个子问题无效时,使用分而治之的方法。当未来要多次使用子问题的结果时,请使用动态方法。
让我们通过一个例子来理解这一点。假设我们试图列出斐波那契数列。然后:
分而治之的方法:
fib(n)
If n < 2, return 1
Else , return f(n - 1) + f(n -2)
动态方法:
mem = [ ]
fib(n)
If n in mem: return mem[n]
else,
If n < 2, f = 1
else , f = f(n - 1) + f(n -2)
mem[n] = f
return f
使用动态方法时,mem 存储每个子问题的结果。
4. 分治算法的优势
- 用朴素方法计算两个矩阵相乘的复杂度为 O(n3),而使用分治法是 O(n2.8074)。这种方法还简化了诸如汉诺塔之类的其他问题。
- 这种方法适用于多处理系统。
- 它有效地利用了内存缓存。
5. 分治的应用
- 二元搜索
- 合并排序
- 快速排序
- 斯特拉森矩阵乘法
- 唐津算法
参考文献
https://www.programiz.com/dsa/divide-and-conquer