編程小結

給定數組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,問最少多少步能夠達到這個數debug

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))

一點感悟

迭代是自下而上,遞歸是自上而下code

求 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)it

等同於求 1,2,3,5,8,13,21,34,55 的fib4(8)io

每遞歸調用一次,都自下而上計算一次console

遞歸的關鍵仍是在於如何劃分子問題,肯定子問題與父問題之間的關係function

相關文章
相關標籤/搜索