用 Swift 写算法——递归和分治法 分治法简介 将问题分解,通过求解局部性的小问题来解决原本的问题,这种技巧叫分治法。实现分治法需要使用递归,其主要步骤如下:
将问题分割成局部问题 (Divide)
递归地求解局部问题 (Slove)
将局部问题的解整合,解决原问题 (Conquer)
应用-穷举搜索 题目:
现有数列 A 和 整数 m。请编写一程序,判断 A 中任意几个元素相加是否能得到 m。A 中每个数只能用一次。
Swift 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 let array = [1 ,5 ,7 ,10 ,21 ]func solve (i: Int, m: Int) -> Bool { if m == 0 { return true } if i >= array.count { return false } let res = solve(i: i + 1 , m: m) || solve(i: i + 1 , m: m - array[i]) return res } solve(i: 0 , m: 8 )
分析:
我们把问题分解成两个更小的局部问题:选择当前元素/不选择当前元素的情况下搜索。如此递归下去,便能解决原问题。
检查所有排列组合需要使用两个递归函数,时间复杂度为 $O(2^n)$ ,因此 n 不能太大。
应用-科赫曲线 题目:
编写一程序,输入整数 n,输出科赫曲线的顶点坐标。
科赫曲线是一种广为人知的不规则碎片形。该图形具有递归结构,可以通过如下方法画出:
给定线段三等分
以三等分点作出正三角形
对新产生的线段重复上述操作
设端点为(0,0)和(100,0)
Swift 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func koch (deep:Int, a:(Double, Double) , b:(Double , Double )) { if deep == 0 { return } let left = ((b.0 - a.0 )/3.0 + a.0 , (b.1 - a.1 )/3.0 + a.1 ) let right = ((a.0 + 2.0 * b.0 )/3.0 , (a.1 + 2.0 *b.1 )/3.0 ) let mid = ((right .0 - left .0 )*cos(1.0 /3.0 *Double .pi) - (right .1 - left .1 )*sin(1.0 /3.0 *Double .pi) + left .0 , (right .0 - left .0 )*sin(1.0 /3.0 *Double .pi) - (right .1 - left .1 )*cos(1.0 /3.0 *Double .pi) + left .1 ) koch(deep: deep - 1 , a: a, b: left ) print (left ) koch(deep: deep - 1 , a: left , b: mid) print (mid) koch(deep: deep - 1 , a: mid, b: right ) print (right ) koch(deep: deep - 1 , a: right , b: b) } koch(deep: 2 , a: (0 ,0 ), b: (100 ,0 ))
分析:
每次都计算出左、中、右三个点的坐标,然后逐层递归。把最原始的问题化成端点到左,左到中,中到右,右到端点这些规模较小的子问题,同时,递归层级递减,直到 0。