分而治之(Divide and conquer)
Divide and conquer(分治法)的基本思想是把一个大问题分解成若干个小问题,把小问题逐个击破后组合起来,得出结果。算法步骤如下:
(1) Divide: 按一定预设条件把问题分解为若干。
(2) Conquer: 解决每个子问题(通常是递归)
(3) Combine: 各子问题的组合
下面举几个例子说明
找金币
给出若干金币,其中有一个是假的,假金币比真金币稍微轻一点。现有一天平,用最快的方法找出假金币。
(1) Divide: 把金币分成相等个数的两堆(若奇数,抽起一块金币)
(2) Conquer: 用天平称,递归地在较轻的一堆中搜索
(3) Combine: 不需要合并。
归并排序(Merge sort)
归并排序是把两个或两个以上有序序列合并成一个新的(规模更大)的有序序列。最初是两个相邻的数归并,接着扩大到四个四个归并,一直扩大到整个序列。
(1) Divide: 不需要分割。
(2) Conquer: 递归地排列子序列,使其有序。(两个数时只需要判断大小来交换)
(3) Combine: 合并两个有序子序列,(把两个子序列元素按大小顺序归并到新序列)
算法规模: T(n) = 2T(n/2) + Θ(n)
由Master method可得 T(n) =Θ(n lgn) (lg为2为底的对数)
归并排序过程:
初始序列[8] [4] [5] [6] [2] [1] [7] [3]
归并到b [4 8] [5 6] [1 2] [3 7]
复制到a [4 8] [5 6] [1 2] [3 7]
归并到b [4 5 6 8] [1 2 3 7]
复制到a [4 5 6 8] [1 2 3 7]
归并到b [1 2 3 4 5 6 7 8]
复制到a [1 2 3 4 5 6 7 8]
指数运算
计算Xn 最直接的方法是把X相乘n次,显然算法规模为Θ(n)。看看分治法如何完成。
(1) Divide: 若n为偶数,分成X n/2 Xn/2 若n为奇数 分成X X n/2 Xn/2
(2) Conquer: 递归地计算 Xn/2
(3) Combine: 把两个Xn/2 相乘,如果n为奇数再乘以X
算法规模: T(n) = T(n/2) +Θ(1)
由Master method 可得 T(n) =Θ(lgn)
二分搜索(Binary search)
二分搜索的思想是先确定待查记录的范围,然后逐步缩小范围直到找到记录为止,适用于有序序列的查找。
(1) divide: 比较待查元素和中间元素,确定待查元素所属的区域。
(2) conquer: 递归地在待查元素所属子序列中搜索
(3) combine: 不需要合并
算法细节可参见任一本数据结构与算法教材。
单峰搜索(Unimodal search)
单峰序列是这样一个序列: 序列某位置m有最大值, m之前为上升序列,m之后为下降序列,好像一个山峰。单峰搜索即找出峰值的位置,其思想跟二分搜索相似,只是判断准则稍微改变(留待读者思考)。
举一个单峰搜索的应用。由一系列点P(x,y)组成的凸多边形(convex, 所有内角>180),按逆时针(或顺时针) 把各点坐标排成一个序列。显然这个序列的x或y分量是一个单峰序列。为找出最左/最右/最上/最下的点,问题就转化为对x或y的单峰搜索。
矩阵相乘 (Matrix multiplication)
按定义进行矩阵相乘的算法复杂度为Θ(n3) . 若把矩阵按某种规则分块,然后逐块相乘,可以有效改进效率。Strassen算法可以把矩阵相乘效率提高到Θ(n2.81),但是该算法存在不少缺点,现时较新的矩阵相乘算法可以到Θ(n2.37)。因篇幅所限,Strassen算法详情请参阅相关资料。
参考书目:
<数据结构算法与应用--C++语言描述> SartajSahni 第14章
<Introduction to Algorithm 2nd edition> MIT chapter 7, chapter 28