zoukankan      html  css  js  c++  java
  • 前端总结数据结构与算法基础

    翻书问题或者走台阶问题。

    问:共有n个台阶,每次只能上1个台阶或者2个台阶,共有多少种方法爬完台阶?共有n页书,每次只能翻1页或者2页书,共有多少种方法翻完全书?
    ps:本质上是斐波那契数列问题。假设只有一个台阶,则只有一种跳法,f(1)=1;如果两个台阶,那么有两种跳法:1,一次跳一级,2,一次跳两级,f(2) = 2。如果大于2的n级台阶,那么一次跳一级台阶,剩下还有n-1级台阶,有f(n-1)种跳法。假如一次跳2级台阶,剩下n-2级台阶,有f(n-2)中跳法。这就表示f(n) = f(n-1)+f(n-2)。

    function fibonacci(n) {
        if (n === 1 || n === 2) {
            return n;
        } else {
            return fibonacci(n-1) + fibonacci(n-2)
        }
    }
    // 一个记忆化的斐波那契数列
    let tem = [0, 1]
    function fibonacci(n) {
       let result = tem[n]
       if(typeof result !== 'number') {
            result = fibonacci(n-1)+fibonacci(n-2)
            tem[n] = result   // 将每次 fibonacci(n) 的值都缓存下来
       }
       return result
    }
    // 动态规划:从底部开始解决问题, 将所有小问题解决掉, 然后合并成一个整体解决方案, 从而解决掉整个大问题 。
    function fibonacci(n) {
        let last = 1;
        let nextLast = 1;
        let result = 1;
        for (let i = 2; i < n; ++i) {
            result = last + nextLast;
            nextLast = last;
            last = result;
        }
        return result;
    }

    二分查找。

    数组array包含了顺序的元素,[1,2,3,...,10],查找目标元素t是否在数组中。
    我们已经提前知道数组是顺序排列的,比如递增顺序。
    时间复杂度为O(logN)
    递推公式:
    f(N) = f(N/2) + O(1) = f(N/4) + 2 * O(1)
    假设 N = 2 ^ M
    最后可以推出
    f(N) = O(logN)


    let list = [1,2,3,4,5,6,7,8,9,10]
    function binarySearch(array, t) {
        let left = 0, right = array.length - 1;
        while(left <= right) {
            let mid = parseInt((right + left)/2)
            if(array[mid] < t) {
                left = mid + 1
            } else if(array[mid] > t) {
                right = mid - 1
            } else {
                return true
            }
        }
        return false
    }
    // 递归写法
    function binarySearch2(array, t, left, right) {
        if(left > right) return -1
        let mid = parseInt((right - left)/2) + left
        if(array[mid] === t) {
            return mid
        }else if(array[mid] < t){
            return binarySearch2(array, t, mid + 1, right)
        }else if(array[mid] > t){
            return binarySearch2(array, t, left, mid - 1)
        }
    }
    let test1 = binarySearch(list, 5)
    let test2 = binarySearch2(list, 5, 0, list.length-1)
    console.log(test1, test2)

    贪心算法--最少硬币找零问题

    所谓贪心,就是先选择当前阶段的最优解,不去考虑这次的选择会不会对未来造成影响,想要通过每个阶段的局部最优解,达到全局最优解。

    假设你为一家自动售货机厂家编程序,自动售货机要每次找给顾客最少数量硬币,美国有以下面额的硬币:1美分、5美分、10美分、25美分。比如说要找36美分的零钱,我们可以用1个25美分、1个10美分和一个1美分。
    (ps:找零问题,先找大额的硬币25分,依次递减)


    function minCoinChange(amount, coins) {
        let minCoin = amount
        let count = 0
        for(let i = 0, l = coins.length; i < l; i++) {
            if (coins[i] <= minCoin) {
                count += Math.floor(minCoin / coins[i])
                console.log(coins[i] + '*' + Math.floor(minCoin / coins[i]))
                minCoin = minCoin % coins[i]
            }
        }
        return count
    }
    let counts = minCoinChange(61, [25, 10, 5, 1])
    console.log(counts)

    动态规划--01背包问题

    function main(volume, value, c){
        let tArray = []
        let useGoodsNo = []
        for(let i = 0, l = volume.length; i <= l; i++) {
            tArray[i] = []
            useGoodsNo[i] = 0
            for(let j = 0; j <= c; j++) {
                if(i == 0 || j == 0){
                    tArray[i][j] = 0
                }
            }
        }
    
        volume.unshift(0)  //让i = 1的时候对应的是1号物品
        value.unshift(0)
        
        for(let i = 1, l = volume.length; i < l; i++) {  // i从1开始 tArray[0][j]已经填满
            for(let j = 1; j <= c; j++) {
                if(j < volume[i]){
                    tArray[i][j] = tArray[i-1][j]
                } else {
                    //如果装了剩下的体积
                    let leftVolume = j - volume[i]
                    // 当前物品的价值
                    let curValue = value[i]
                    // 剩下的体积的价值是 (tArray[i - 1]因为0 1背包,不能重复放同个物品)
                    let leftValue = tArray[i - 1][leftVolume]
    
                    tArray[i][j] = Math.max(tArray[i-1][j], curValue+leftValue)
                }
            }
        }
        // 填满表格
        console.table(tArray)
        // 逆向获取物品
        let C = c
        for(let i = value.length-1; i > 0; i--){
            if(tArray[i][C] > tArray[i-1][C]){
                useGoodsNo[i] = 1
                C = C - volume[i]
            }
        }
        console.table(useGoodsNo)  // 所选的物品
        console.log(tArray[value.length-1][c])  // 最大价值
    }
    main([2,3,4,5], [3,4,5,6], 8)

    展平数组

    // 展平数组 [[1, 2], 3, [[[4], 5]]] => [1, 2, 3, 4, 5]
    function flatten(arr) {
        return [].concat(
            ...arr.map(x => Array.isArray(x) ? flatten(x) : x)
        )
    }

    随机打乱数组

    function shuffle(arr) {
        for(let i = 0; i < arr.length-1; i++){
            const j = i + Math.floor(Math.random() * (arr.length - i))
            [arr[i], arr[j]] = [arr[j], arr[i]]
        }
    }

    函数节流

    // 函数节流
    function throttle(func, delay = 60) {
        let lock = false
        return (...args) => {
            if(lock) return
            func(...args)
            lock = true
            setTimeout(()=> {
                lock = false
            }, delay)
        }
    }

    柯里化

    //对于curry(foo),g函数参数足够4个,就调用foo(a,b,c,d),如果小于4就返回一个可以继续积累参数的函数
    const curry = func => {
        const g = (...allArgs) => allArgs.length >= func.length ?
        func(...allArgs) : (...args) => g(...allArgs, ...args)
        return g
    } 
    const foo = curry((a, b, c, d) =>{
        console.log(a, b, c, d)
    })
    foo(1)(2)(3)(4) // 1 2 3 4
    foo(1)(2)(3)  // 不返回
    const f = foo(1)(2)(3)
    f(4) // 1 2 3 4 5

    Y组合子

    const y = function(le) {
        return function (f) {
            return f(f)
        }(function (f) {
            return le(
                function(...x) {
                    return (f(f))(...x)
                }
            )
        })
    }
    const curryY = func => y(g => {
            (...allArgs) => {
                allArgs.length >= func.length ? func(...allArgs) : (...args) =>g(...allArgs, ...args)
            }
        }
    )
    const foo = curryY((a, b, c, d) => {
        console.log(a, b, c, d)
    })
    foo(1)(2)(3)(4)

    链表

    链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。

    function ListNode(val) {
        this.val = val
        this.next = null
    }
    let node1 = new ListNode(1)
    let node2 = new ListNode(2)
    let node3 = new ListNode(3)
    node1.next = node2
    node2.next = node3
    console.log(node1)
    
    function recursiveTraverse(head) {
        if(head != null) {
            console.log(head.val)
            recursiveTraverse(head.next)
        }
    }
    recursiveTraverse(node1)
    
    // 翻转一条单向链表
    // 输入 1 -> 2 -> 3 -> null
    // 输出 3 -> 2 -> 1 -> null
    function reverseLinkedList(head){
        let dummy = head
        let tem = dummy
        while(head != null && head.next != null){
            dummy = head.next
            head.next = dummy.next
            dummy.next = tem
            tem = dummy
        }
        return dummy
    }
    let reverseLink = reverseLinkedList(node1)
    console.log(reverseLink)

    二叉树

    function TreeNode(val) {
        this.val = val
        this.left = null
        this.right = null
    }
    let root = new TreeNode(2)
    root.left = new TreeNode(1)
    root.right = new TreeNode(3)
    console.log(root)
    /*
    *     2
    *   /   
    *  1     3
    */
    
    /*
        中序遍历:(
            定义:
            1,中序遍历左子树
            2,遍历根节点
            3,中序遍历右子树
        )
        前序遍历:(
            定义:
              1,遍历根节点
              2,前序遍历左子树
              3,前序遍历右子树
        )
        后序遍历:(
            定义:
              1,后序遍历左子树
              2,后序遍历右子树
              3,遍历根节点
        )
    */
    
    function inOrder(root) {
        if(root) {
            console.log('前',root.val)  // 2 1 3
            inOrder(root.left)
            console.log('中',root.val)  // 1 2 3
            inOrder(root.right)
            console.log('后',root.val)  //  1 3 2
        }
    }
    inOrder(root)
    
    /*
        二叉查找树/二叉搜索树:(
            定义:
            左子树的所有节点的值小于根节点
            右子树的所有节点的值大于根节点
            左子树和右子树都是二叉查找树
        )
        二叉搜索树的中序遍历就是排好序的元素。
    */
    function binarySearchTreeFind(root, target) {
        if(!root) return false
        if(root.val === target) {
            return true
        } else if(root.val < target){
            return binarySearchTreeFind(root.right, target)
        } else if(root.val > target) {
            return binarySearchTreeFind(root.left, target)
        }
    }
    console.log(binarySearchTreeFind(root, 1))

    First In Last Out(FILO)
    先进后出,后进先出

    function Stack(){
        this.stack = []
    
        this.isEmpth = function(){
            return this.size() === 0
        }
        this.size = function() {
            return this.stack.length
        }
        // 出栈
        this.pop = function(){
            if(this.isEmpth()){
                return null
            } else {
                return this.stack.pop()
            }
        }
        // 进栈
        this.push = function (val){
            return this.stack.push(val)
        }
            // 返回栈顶元素
        this.peak= function(){
            if(this.isEmpth()) {
                return null
            } else {
                return this.stack[this.stack.length - 1]
            }
        }
    }
    
    let test = new Stack()
    test.push(1)
    console.log(test.peak())
    console.log(test.isEmpth())
    test.pop()
    console.log(test.peak())

    队列

    First In First Out(FIFO)
    先进先出

    function Queue() {
        this.queue = []
    
        this.size = function() {
            return this.queue.length
        }
        this.isEmpty = function(){
            return this.size() === 0
        }
        // 入队列
        this.enqueue = function (val){
            this.queue.unshift(val)
        } 
        // 出队列
        this.dequeue = function(){
            if(this.isEmpty()){
                return null
            } else {
                return this.queue.pop()
            }
        }
    }
    let test = new Queue()
    test.enqueue(1)
    test.enqueue(2)
    console.log(test.queue)
    test.dequeue()
    console.log(test.queue)




  • 相关阅读:
    循环
    list和tuple
    Python字符串和编码注意点
    【转载】国内安卓推送的希望
    【转载】Android属性动画和视图动画的区别
    【转载】Android 属性动画详解
    【转载】 android 属性动画详解
    java8 新特性学习详细篇2
    java8 新特性详细篇
    JAVA8十大新特性
  • 原文地址:https://www.cnblogs.com/lguow/p/13203451.html
Copyright © 2011-2022 走看看