斐波拉契数列
这个数列生成规则很简单,每一项都是前两项的和,举例
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……
用数学符号来描述更好
$$ F_{n}=F_{{n-1}}+F_{{n-2}}(n≧2) $$
递归方法求解
这个几行代码就可以解决
// n从1开始
function fib(n) {
if (n <= 2)
return 1;
return fib(n - 1) + fib(n - 2);
}
但是分析一下这个算法的执行过程,以f(6)为例
以上的递归树中,很多节点被重复计算,譬如f(2)就被重复执行了5次,另外,递归调用中每一个函数都要保留上下文,所以空间上开销也不小。所以这个方法并不是很理想。
循环方法求解
其实还是利用斐波拉契数列的特性
// n从1开始
function fibFor(n) {
if (n <= 2)
return 1;
let arr = [];
//arr[0] = 0;
arr[2] = arr[1] = 1;
for (let i = 3; i <= n; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n];
}
当然这个方法还可以压缩一下,因为我们没必要存储一个数组,只用三个变量就行了。
// n从1开始
function fibFor2(n) {
if (n <= 2)
return 1;
let before2 = 1;
let before1 = 1;
let now = null;
for (let i = 3; i <= n; i++) {
now = before1 + before2;
before2 = before1;
before1 = now;
}
return now;
}
这其实就是动态规划的思想了,自底向上,用子问题解决父问题。
进一步:LCS问题
最长公共子序列(Longest Common Subsequence)是指,给出两个字符串,找到最长公共子序列(LCS),返回LCS的长度,注意是子序列,不是子串。
举个例子:
对于”ABCD” 和 “EDCA”,这个LCS是 “A” (或 D或C),返回1
对于“ABCD” 和 “EACB”,这个LCS是“AC“(或”AB“)返回2
思路
对于这个问题,设字符串a长度为m,字符串b为n,考察它们的最后一个字符串,如果a[m]==b[n],则Longestm = Longestm-1,否则,Longestm = Max(Longestm-1, Longestm)。找到这个关系后,首先想到的是用递归方法解决问题:
package main
import "fmt"
func max(x, y int) int {
if x > y {
return x
}
return y
}
func LongestCommonSequence(a string, b string) int {
aChars := []rune(a)
bChars := []rune(b)
aLen := len(aChars)
bLen := len(bChars)
if aLen == 0 || bLen == 0 {
return 0
}
if aChars[aLen-1] == bChars[bLen-1] {
return LongestCommonSequence(string(aChars[:aLen-1]), string(bChars[:bLen-1])) + 1
} else {
return max(
LongestCommonSequence(string(aChars[:aLen-1]), b),
LongestCommonSequence(a, string(bChars[:bLen-1])))
}
}
func main() {
a := "ACDE"
b := "CBE"
fmt.Printf("Longest sub sequence length of %s and %s is %d\n", a, b,
LongestCommonSequence(a, b))
}
这样的解法跟上面斐波拉契的解法一样,会遇到子问题重复计算,而且递归栈要保存临时结果,会占用较大的内存。所以,我们也可以思考一下使用自底而上的方法。考察一下递归表达式,它涉及到两个变量(m,n),所以我们可以用一个二维数组来保存之前的结果。
实现:
func LongestCommonSequenceDP(a string, b string) int {
aChars := []rune(a)
bChars := []rune(b)
aLen := len(aChars)
bLen := len(bChars)
if aLen == 0 || bLen == 0 {
return 0
}
record := make([][]int, aLen+1)
for i := 0; i <= aLen; i++ {
record[i] = make([]int, bLen+1)
record[i][0] = 0
}
for j := 0; j <= bLen; j++ {
record[0][j] = 0
}
for i := 1; i <= aLen; i++ {
for j := 1; j <= bLen; j++ {
if aChars[i-1] == bChars[j-1] {
record[i][j] = record[i-1][j-1] + 1
} else {
record[i][j] = max(record[i][j-1], record[i-1][j])
}
}
}
return record[aLen][bLen]
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。