记录 2 道算法题
第 k 个语法符号
n是第几行, k是第几个
0会在下一行变成01,1会在下一行变成10
第一行: 0
第二行: 01
第三行: 0110
第四行: 01101001
想知道当前第 n 行,第 k 个是多少首先要知道 n - 1 行,即上一行是 0 还是 1, 然后还要知道第 k 个是上一行分裂的数的第一位还是第二位。
假如上一行是 1,那么这一行就要分裂成 10 ,然后要考虑第 k 个是 1 还是 0,即是奇数位还是偶数位。
分成两两一组的话,第 k 个除以 2 如果是小数,则说明是下一组的第一个。如果是整数,说明是这一组的第二个。
比如 第 4 行,第 5 个。 是位于第 3 组的 10 或 01 里面。
所以 第 k 个对应的上一行是 Math.ceil(k / 2)。
这种层层递进的可以通过递归来寻找,正好传入 n - 1 可以作为终点。因为找到第二行的时候,只有两种结果。
递归注意的点是, 终止条件,每一个单元的操作。
function kthGrammar(n, k) {
if (n === 1) {
return 0
}
if (n === 2) {
// 第二行只有 01
if (k === 1) {
return 0
} else {
return 1
}
}
// 我们希望 kthGrammar 可以返回上一行是 1 是 0,
// 然后根据 k 所在是奇数位还是偶数位,得到上一行的 1 或 0 分裂的对应的数
let i = kthGrammar(n - 1, Math.ceil(k / 2))
i = i === 1 ? [1, 0] : [0, 1]
return k % 2 === 1 ? i[0] : i[1]
}
其实还有一种找规律的解法。就是每一行的前一半都和上一行相等。然后后一半都是前一半的每一位相反。
所以可以判断 k 是在前一半还是后一半,如果是后一半就等于前一半相同位置的相反。等于上一行的同一位置的相反。如果到了上一行发现是上一行的后一半。则和前面相同的过程处理一遍。
又套起来了。
斐波那契数列
f(0) = 0 f(1) = 1 f(n) = f(n - 1) + f(n - 2)
要求结果取余 1000000007
有两种解法,一种是递归,一种是循环
递归版本
// 用 map 来减少重复计算
const map = new Map([[0, 9], [1, 1]])
function lib(n) {
if (map.has(n)) {
return map.get(n)
} else {
const c = (lib(n - 1) + lib(n - 2)) % 1000000007
map.set(n , c)
return c
}
}
循环版本
function fib(n) {
if (n === 0) {
return 0
}
if (n === 1) {
return 1
}
let a = 0,b = 1
// 0 1 1 2 3 5
// a b c
// c 是当前的位置。 a 和 b 等价于 n-1 和 n-2
for (let i = 2; i <= n+1; i++) {
const c = (a + b) % 1000000007 /* leetcode 要求取余 */
a = b
b = c
}
return b
}
结束