算法二

作者: koala949 | 来源:发表于2020-09-29 16:10 被阅读0次

    递归表达式

    n! = n*(n-1)! (0!=1)

    fn  =  {
          1, n=1,
          2, n=2,
          f(n-1)+f(n-2), n>2
     }
    

    递归经常需要初始条件以及递归表达式。

    // 错误写法:
    function fibonacci(n) {
      return n == 1 || n == 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2);
    }
    // 对性能的思考:转换成树
    // f(5)f(4)执行了1次
    // f(3)执行了2次
    // f(2)执行了3次
    // f(1)执行了2次
    
    // 递归次数:N>=1+2+4+8+...+2^(n-2)=(1- 2^(n-1))/(2^(n-1) -1)
    // 2^10=1024
    // 2^20= 1048576
    

    从低端构造递归,斐波拉契数列, reduce和for循环两种写法,本质一样。

    1.
    function fibonacci(n) {
      let [a, b] = [0, 1];
      for (let i = 0; i < n; i++) {
        [a, b] = [b, a + b];
      }
      return b;
    }
    2.
    function fibonacci(n) {
      return Array(n)
        .fill()
        .reduce(
          ([a, b], _) => {
            return [b, a + b];
          },
          [0, 1]
        )[1];
    }
    
    

    深度拷贝

    function clone(obj) {
      if (obj == null || typeof obj !== "object") return obj;
      const newObj = new obj.constructor();
      for (let key in Object.getOwnPropertyDescriptors(obj)) {
        newObj[key] = clone(obj[key]);
      }
      return newObj;
    }
    

    深度比较

    function deepCompare(a, b) {
      if (
        a === null ||
        typeof a !== "object" ||
        b === null ||
        typeof b !== "object"
      ) {
        return a === b;
      }
      const propsA = Object.getOwnPropertyDescriptors(a);
      const propsB = Object.getOwnPropertyDescriptors(b);
      if (Object.keys(propsA).length !== Object.keys(propsB).length) {
        return false;
      }
      return Object.keys(propsA).every((key) => deepCompare(a[key], b[key]));
    }
    

    树的算法和前端

    • DOM
    • 选择器
    • JSON
    • 虚拟DOM
    • 文本查找和输入提示

    树的递归表示

    T: v, [T1,...Tk] 数含有值V和一个子树的列表

    class Tree {
      constructor(v, children) {
        this.v = v;
        this.children = children || null;
      }
    }
    
    const tree = new Tree(10, [
      new Tree(5),
      new Tree(3, [new Tree(7), new Tree(11)]),
      new Tree(2),
    ]);
    
    

    树的遍历(先序)

    function tree_transverse(tree){
        console.log(tree.v)
        tree.children && tree.children.forEach(tree_transverse)
    } // => 10, 5, 3, 7, 11, 2  
    

    树的遍历(后序)

    function tree_transverse(tree){
        tree.children && tree.children.forEach(tree_transverse)
        console.log(tree.v)
    }    // => 5 7 11 3 2 10  
    

    树的遍历(中序)

    /**
    * ord:根节点打印的位置
    **/
    function tree_transverse_m(tree, ord = 0) {
      let trans = false;
      if (!tree.children) {
        console.log(tree.v);
        return;
      }
      tree.children.forEach((child, i) => {
        if (i === ord) {
          trans = true;
          console.log(tree.v);
        }
        tree_transverse_m(child, ord);
      });
      !trans && console.log(tree.v);
    }
    
    tree_transverse_m(tree, 0) //=> 10 5 3 7 11 2
    tree_transverse_m(tree, 3) //=> 5  7 11 3 2 10(后序)
    tree_transverse_m(tree, 1) //=> 5 10 7 3 11 2
    

    树的查找

    
    function find(tree, prediction) {
      return [...tree_transverse(tree).find(prediction)];
    }
    
    function find(tree, prediction) {
      for (let node of tree_transverse(tree)) {
        if (prediction(node)) return node;
      }
    }
    
    find(tree, (node) => node.v === 2);
    

    树方法:

    /**
     * 映射树状结构数据(类似数组的map方法)
     * @param {object[]} tree - 树状结构的数据
     * @param {function} iteratee - 映射单个节点的方法
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @param {object[]} acc - 当前节点的所有父节点集合
     * @returns {object[]}
     */
    export function mapTree(tree, iteratee, { childrenName = 'children' } = {}, acc = []) {
      return tree.map((node) => {
        const children = node[childrenName];
        if (children && children.length) {
          return iteratee.call(null, {
            node,
            acc,
            hasChildren: true,
            children: mapTree(children, iteratee, { childrenName }, acc.concat(node)),
          });
        }
        return iteratee.call(null, {
          node,
          acc,
          hasChildren: false,
        });
      });
    }
    
    /**
     * 遍历树状结构数据(类似数组的forEach方法)
     * @param {object[]} tree - 树状结构的数据
     * @param {function} iteratee - 处理单个节点的方法
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @param {object[]} acc - 当前节点的所有父节点集合
     * @returns {object[]}
     * @example
     * forEachTree((arr, ({ node, acc })) => console.log(node, acc))
     */
    export function forEachTree(tree, iteratee, { childrenName = 'children' } = {}, acc = []) {
      tree.forEach((node) => {
        iteratee.call(null, { node, acc });
        const children = node[childrenName];
        if (children && children.length) {
          forEachTree(children, iteratee, { childrenName }, acc.concat(node));
        }
      });
    }
    
    /**
     * 过滤树状结构数据(类似数组的filter方法)
     * @param {*} tree - 树状结构的数据
     * @param {*} iteratee - 匹配目标节点的方法
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @returns {object[]}
     */
    export function filterTree(tree, iteratee, { childrenName = 'children' } = {}) {
      function filter(list, acc) {
        const res = [];
        list.forEach((one) => {
          const children = one[childrenName];
          const _children = children && children.length ? filter(children, acc.concat(one)) : [];
    
          let tmp;
          if (_children && children && _children.length === children.length) {
            tmp = one;
          } else {
            tmp = {
              ...one,
              [childrenName]: _children,
            };
          }
    
          if (iteratee.call(null, one, acc) || _children.length) {
            res.push(tmp);
          }
        });
        return res;
      }
      return filter(tree, []);
    }
    
    /**
     * 将树状结构数据转化成数组
     * @param {*} tree - 树状结构的数据
     * @param {*} iteratee - 转化单个节点的数据
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @returns {object[]}
     * @example
     * // returns [1,2,3,4,5]
     * treeToList(arr, node => node.id)
     */
    export function treeToList(tree, iteratee, { childrenName = 'children' } = {}) {
      const acc = [];
      forEachTree(
        tree,
        ({ node }) => {
          if (isFunction(iteratee)) node = iteratee.call(null, node);
          acc.push(node);
        },
        { childrenName },
      );
      return acc;
    }
    
    /**
     * 查找节点(类似数组的find方法)
     * @param {*} tree - 树状结构的数据
     * @param {*} iteratee - 匹配目标节点的方法
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @returns {{ node, acc }}
     * @example
     * // returns { node, acc }
     * findTree(arr, node => node.id == '1')
     */
    export function findTree(tree, iteratee, { childrenName = 'children' } = {}) {
      let _node = null;
      let _acc = [];
      try {
        forEachTree(
          tree,
          ({ node, acc }) => {
            if (iteratee.call(null, node, acc)) {
              _node = node;
              _acc = acc.concat(node);
              throw new Error();
            }
          },
          { childrenName },
        );
      } catch (e) {
        // do nothing
      }
      if (!_node) return { node: null, acc: [] };
      return { node: _node, acc: _acc };
    }
    
    /**
     * 数组转换成树状结构(已废弃)
     * @param {object[]} list - 数组
     * @param {function} isTopNode - 返回是否为顶层节点
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @param {function} param2.sortRule - 同级元素的排序规则
     * @returns {object[]}
     */
    export function listToTree2(
      list,
      isTopNode,
      { childrenName = 'children', sortRule, id = 'id', parentId = 'parentId' } = {},
    ) {
      if (!isArray(list)) return [];
      // 获取所有id
      const idSet = new Set();
      list.forEach((one) => one[id] && idSet.add(one[id]));
    
      const _isTopNode = (node) => {
        if (isFunction(isTopNode)) return isTopNode(node);
        return (
          isNullOrUndefined(node[parentId]) ||
          !idSet.has(node[parentId]) ||
          node[parentId] === -1 ||
          node[parentId] === '-1'
        );
      };
      const firstLevelNodes = list.filter(_isTopNode);
      function findChildren(node) {
        const children = list.filter((one) => one[parentId] === node[id]);
        return transform(children);
      }
      function transform(list) {
        const _list = list.map((one) => ({
          ...one,
          children: findChildren(one),
        }));
        if (isFunction(sortRule)) return _list.sort(sortRule);
        return _list;
      }
      return transform(firstLevelNodes);
    }
    
    /**
     * 数组转换成树状结构
     * @param {object[]} list - 数组
     * @param {function} isTopNode - 返回是否为顶层节点
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @param {function} param2.sortRule - 同级元素的排序规则
     * @returns {object[]}
     */
    export function listToTree(
      list,
      isTopNode,
      { childrenName = 'children', sortRule, id = 'id', parentId = 'parentId' } = {},
    ) {
      if (!isArray(list)) return [];
      const _isTopNode = (node) => {
        if (isFunction(isTopNode)) return isTopNode(node);
        return node[parentId] === -1 || node[parentId] === '-1' || isNullOrUndefined(node[parentId]);
      };
    
      const idMapping = list.reduce((acc, current) => {
        acc[current[id]] = current;
        return acc;
      }, {});
    
      const firstLevelNodes = [];
      list.forEach((one) => {
        if (_isTopNode(one)) {
          firstLevelNodes.push(one);
        }
        const parentEl = idMapping[one[parentId]];
        if (parentEl) {
          parentEl[childrenName] = [...(parentEl[childrenName] || []), one];
        }
      });
    
      function sort(list) {
        if (!isArray(list)) return [];
        const _list = list.map((one) => ({
          ...one,
          [childrenName]: sort(one[childrenName]),
        }));
        return _list.sort(sortRule);
      }
    
      if (isFunction(sortRule)) return sort(firstLevelNodes);
      return firstLevelNodes;
    }
    
    /**
     * 查找目标节点的上一节点
     * @param {*} tree - 树状结构的数据
     * @param {*} iteratee - 匹配目标节点的方法
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @returns {null|object}
     */
    export function findPrevNode(tree, iteratee, { childrenName = 'children' } = {}) {
      if (!isArray(tree)) return null;
      function _findPrevNode(data) {
        let prevNode = null;
        const res = data.some((one) => {
          if (iteratee.call(null, one)) {
            return true;
          }
          const children = one[childrenName];
          if (children && children.length) {
            const [success, _prevNode] = _findPrevNode(children);
            if (success) {
              // 从当前节点的子节点中找到目标节点,则返回目标节点前一个
              // 若目标节点为子节点中的第一个,则返回当前节点
              prevNode = _prevNode || one;
              return true;
            }
            // 如果子节点中未找到目标节点,将子节点中的最后一个节点设置为上一节点
            prevNode = _prevNode;
            return false;
          }
          // 不匹配目标节点,且没有子节点,将当前节点设置为上一节点
          prevNode = one;
          return false;
        });
        return [res, prevNode];
      }
      return _findPrevNode(tree)[1];
    }
    
    /**
     * 查找目标节点的下一节点
     * @param {*} tree - 树状结构的数据
     * @param {*} iteratee - 匹配目标节点的方法
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @returns {null|object}
     */
    export function findNextNode(tree, iteratee, { childrenName = 'children' } = {}) {
      if (!isArray(tree)) return null;
      function _findNextNode(data) {
        let nextNode = null;
        const res = data.some((one, index) => {
          const children = one[childrenName];
          if (iteratee.call(null, one)) {
            // 匹配到目标节点
            if (children && children.length) {
              // 有子节点,取子节点中的第一个
              nextNode = children[0];
            } else {
              // 无子节点,取下一个同级节点
              nextNode = data[index + 1];
            }
            return true;
          }
          if (children && children.length) {
            const [success, _nextNode] = _findNextNode(children);
            if (success) {
              // 从当前节点的子节点中找到目标节点
              // 如果子节点中的目标节点存在下一节点,则取下一节点
              // 如果子节点中的目标节点为最后一个节点,则取当前节点的下一同级节点
              nextNode = _nextNode || data[index + 1];
              return true;
            }
          }
          return false;
        });
        return [res, nextNode];
      }
      return _findNextNode(tree)[1];
    }
    
    /**
     * 查找目标节点的所有兄弟节点(包括自身)
     * @param {*} tree - 树状结构的数据
     * @param {*} iteratee - 匹配目标节点的方法
     * @param {object} param2
     * @param {string} param2.childrenName - 挂载子节点的字段名
     * @returns {object[]}
     */
    export function findSiblings(tree, iteratee, { childrenName = 'children' } = {}) {
      if (!isArray(tree)) return [];
      function _findSiblings(data) {
        let res = null;
        data.some((one) => {
          if (iteratee.call(null, one)) {
            res = data;
            return true;
          }
          const children = one[childrenName];
          if (children && children.length) {
            res = _findSiblings(children);
            return Boolean(res);
          }
          return false;
        });
        return res;
      }
      return _findSiblings(tree);
    }
    
    /**
     * 查找目标节点的所有父级节点(包括自身)
     * @param {*} tree - 树状结构的数据
     * @param {*} iteratee - 匹配目标节点的方法
     * @returns {object[]}
     */
    export function treeFindPath(
      tree,
      id,
      { idName = 'id', parentId = 'parentId', childrenName = 'children' } = {},
    ) {
      if (tree && id) {
        let parentItems = [];
        let forFn = function (arr, id) {
          for (let i = 0; i < arr.length; i++) {
            let item = arr[i];
            if (item[idName] === id) {
              parentItems.push(item);
              forFn(tree, item[parentId]);
              break;
            } else {
              if (item[childrenName]) {
                forFn(item[childrenName], id);
              }
            }
          }
        };
        forFn(tree, id);
        return parentItems;
      }
      return [];
    }
    

    相关文章

      网友评论

          本文标题:算法二

          本文链接:https://www.haomeiwen.com/subject/ftbruktx.html