给定数组arr,取n个数,和为sum,有哪些种取法
递归解法
function main(arr, sum, n) {
let result = []
if (n === 1) {
arr.filter(item => item === sum)
.forEach(item2 => result.push([item2]))
return result
}
for (let i = 0; i < arr.length; i++) {
let el = arr[i]
let subArr = arr.slice()
subArr.splice(i, 1)
let subResult = main(subArr, sum - el, n - 1)
if (subResult.length > 0) {
subResult.forEach(item3 => {
item3.unshift(el)
result.push(item3)
})
}
}
return result
}
let result = main([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 18, 3)
let result2 = result.map(item => item.sort().join(","))
console.log([...new Set(result2)])
递归优化,计算过程中去重
思路一的做法,存在大量的重复,实际上对 for 循环做一点修改,就可以在过程中避免重复
function main(arr, n, sum) {
if (n === 1) {
return arr.includes(sum) ? [
[sum]
] : []
}
let result = []
for (let i = 0; i < arr.length - n; i++) {
let arr1 = arr.slice(i + 1)
let addend = arr[i]
let arr2 = main(arr1, n - 1, sum - addend)
arr2.forEach(r => {
r.unshift(addend)
result.push(r)
})
}
return result
}
let result = main([1, 2, 3, 4, 5, 6, 7, 8, 9], 3, 15)
debugger
迭代
function main(arr, n, sum) {
let result = []
let step = 1
let stack = [{
sum: [],
arr
}]
while (step < n) {
let newStack = []
stack.forEach(s => {
for (let i = 0; i < s.arr.length; i++) {
let newSum = [...s.sum, s.arr[i]]
if (newSum.reduce((a, b) => a + b, 0) < sum) {
newStack.push({
sum: newSum,
arr: s.arr.slice(i + 1)
})
}
}
})
stack = newStack
step++
}
stack.forEach(s => {
for (let i = 0; i < s.arr.length; i++) {
let newSum = [...s.sum, s.arr[i]]
if (newSum.reduce((a, b) => a + b, 0) === sum) {
result.push([...s.sum, s.arr[i]])
}
}
})
return result
}
let result = main([1, 2, 3, 4, 5, 6, 7, 8, 9], 3, 15)
debugger
砝码问题
给一组砝码,给一个重量,问用该组砝码能否称出该重量。例如,一组砝码 [1,3],一个重量 2,返回 true
递归
function main(arr, weight) {
// 终止条件
if (arr.length === 0) {
return false
}
if (arr.length === 1) {
return arr[0] === weight ? true : false
}
if (arr.includes(weight)) {
return true
}
let item = arr[0]
if (main(arr.slice(1), weight)) {
return true
}
if (main(arr.slice(1), weight + item)) {
return true
}
if (main(arr.slice(1), weight - item)) {
return true
}
return false
}
let result = main([1, 2, 10], 6)
debugger
迭代
function main(arr, weight) {
let stack = [0]
let i = 0
while (i < arr.length) {
let stackCopy = []
for (let j = 0; j < stack.length; j++) {
let s = stack[j]
if (s === weight) {
return true
}
if (s + arr[i] === weight) {
return true
}
if (s - arr[i] === weight) {
return true
}
stackCopy.push(s + arr[i])
stackCopy.push(s - arr[i])
stackCopy.push(s)
}
stack = stackCopy
i++
}
return false
}
let result = main([1, 2, 10], 3)
debugger
+1,*2计算最少步数
给一个数,只能从1开始,+1或者*2,问最少多少步可以达到这个数
function main(x) {
let result = []
result.push(Math.ceil(x / 2))
// 7 => (1+1+1)*2+1 共4步
// 12 => (1+1+1+1+1+1)*2 共6步
let i = 1
let count = 0
while (i * 2 <= x) {
i = i * 2
count++
}
count = count + x - i
result.push(count)
return result
// 用 +1 的方法,步数始终是 Math.ceil(x/2)
// 用 *2 后 +1 的方法,主要是考虑当 2^(n+1) > x > 2^n 时,从 2^n 到 x 需要进行多少次 +1
}
let result = main(15)
debugger
斐波那契数列
传统递归方法
function fib1(n) {
if (n < 2) {
return n
}
return fib1(n - 1) + fib1(n - 2)
}
console.log(fib1(10))
迭代
function fib2(n) {
if (n < 2) {
return n
}
let arr = [0, 1]
for (let i = 2; i < n + 1; i++) {
arr.push(arr[i - 1] + arr[i - 2])
}
return arr[n]
}
console.log(fib2(10))
迭代优化
function fib3(n) {
if (n < 2) {
return n
}
let arr = [0, 1]
for (let i = 2; i < n; i++) {
let sum = arr[0] + arr[1]
arr[0] = arr[1]
arr[1] = sum
}
return arr[0] + arr[1]
}
console.log(fib3(10))
递归优化一
function fib4(n, p, k) {
if (n === 1) {
return k
}
if (n === 0) {
return p
}
return fib4(n - 1, k, p + k)
}
console.log(fib4(10, 0, 1))
递归优化二
function fib5(n, p, k) {
if (n === 1) {
return k
}
if (n === 0) {
return p
}
return fib5(n - 2, p + k, p + k + k)
}
console.log(fib5(10, 0, 1))
一点感悟
迭代是自下而上,递归是自上而下
求 0,1,1,2,3,5,8,13,21,34,55 的fib4(10)
等于求 1,1,2,3,5,8,13,21,34,55 的fib4(9)
等同于求 1,2,3,5,8,13,21,34,55 的fib4(8)
每递归调用一次,都自下而上计算一次
递归的关键还是在于如何划分子问题,确定子问题与父问题之间的关系