浪漫|递归之美,在于你会

·  阅读 1994

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

递归之美,在于你会

tips:每个技术点都值得优学优写:6期

什么是递归

Mozilla 上这样解释递归(Recursion)这个术语:

一种函数调用自身的操作。递归被用于处理包含有更小的子问题的一类问题。

一个递归函数可以接受两个输入参数:一个最终状态(终止递归)或一个递归状态(继续递归)。

我的理解是:递归就是有条件的自己调用自己。 所谓有条件就是可以终止,不会出现死循环, 符合条件继续自己调用自己,不符合调用就到此为止。

递归应用场景

tips:学递归,要知道递归的用法,以及递归的应用场景。

学会递归的标志之一,是遇到适合的场景能想起来该用递归了。

数据:处理一些具有层级递进特征的数据,例如 tree 数据,有时会比较方便。

数学: 8皇后问题,汉诺塔,阶乘问题,迷宫问题。

算法:比如快排,归并排序,二分查找,分治算法等。

递归应用与反扁平化示例

tips:我对递归好很有好感,因为好用,物超所值。 在处理有层级的,每个层级的结构相同类似的数据时,非常棒。下面将举几个例子。

递归应用示例:tree 数据扁平化

先看下递归应用前后,扁平化数据前后的效果图

image.png

部分关键代码

  /**
   *将 tree 数据扁平化
   * @param tree 需要被扁平化的具有层级结构的 tree 数据
   * @param newList 接收扁平数据的数组
   */
  function treeTranslate (tree, newList) {
    // (tips:Mozilla上关于递归的这个术语,解释说一个递归函数可以接受两个输入参数:一个最终状态(终止递归)或一个递归状态(继续递归)。)
    // 从两个入参的角度看,newList.length>10000 为真可以理解为“最终状态”。
    // 这个条件设置无关业务,是出于极限保护设置的,可不设。因为假设这个 tree 数据扁平化后不可能超过 100000,
    // 一旦超过说明出错了,所以做出了这个极限保护条件。
    if (newList.length > 100000) {
      return
    }

    tree.map(e => {
      newList.push(e)

      // 自己调用自己,条件是 e.children.length 为真
      // 从两个入参的角度看,e.children.length 为真可以理解为“递归状态”。
      if (e.children && e.children.length) {
        treeTranslate(e.children, newList)
      }
    })
  }
复制代码

反扁平化示例:将扁平数据转换为 tree 数据

先看下反扁平化前后效果图

image.png

部分关键代码

  /**
   * 反扁平化:将扁平数据转换为 tree 数据
   * @param list 扁平数据
   */
  function toTree (list) {
    let tree = []
    list.map(e => {
      // 以 e.pid===null,作为判断是否根节点的依据,或者直接写死根节点(如果确定的话),
      // 具体以什么为根节点的依据得看数据的设计规则,通常是判断层级或是否根节点的标记
      if (e.pid === null) {
        tree.push(e)
      }

      list.map(e2 => {
        if (e2.pid === e.id) {
          // 避免出现重复数据
          const index = e.children.findIndex(sub => sub.id === e2.id)
          if (index === -1) {
            e.children.push(e2)
          }
        }
      })
    })

    console.log("反扁平化后:",tree)
  }
复制代码

完整 demo 示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>递归(Recursion) Demo</title>
</head>
<body>
<h1>递归(Recursion) Demo</h1>
<script>
  window.onload = function () {
    init()
  }

  function init () {
    let newList = []
    console.log('扁平化前:', treeList)
    treeTranslate(treeList, newList)
    console.log('扁平化后:', newList)

    // 反扁平化
    toTree(JSON.parse(JSON.stringify(newList)))
  }

  const treeList = [
    {
      id: 1,
      pid: null,
      label: '一级',
      value: '1',
      flag: true,
      children: [
        {
          id: 2,
          pid: 1,
          label: '二级1',
          value: '2.1',
          flag: false,
          children: []
        },
        {
          id: 3,
          pid: 1,
          label: '二级2',
          value: '2.2',
          flag: true,
          children: []
        },
        {
          id: 4,
          pid: 1,
          label: '二级3',
          value: '2.3',
          flag: true,
          children: [
            {
              id: 5,
              pid: 4,
              label: '三级1',
              value: '3.1',
              flag: true,
              children: []
            },
            {
              id: 6,
              pid: 4,
              label: '三级2',
              value: '3.2',
              flag: true,
              children: []
            },
          ]
        },
      ]
    }
  ]

  /**
   *将 tree 数据扁平化
   * @param tree 需要被扁平化的具有层级结构的 tree 数据
   * @param newList 接收扁平数据的数组
   */
  function treeTranslate (tree, newList) {
    // (tips:Mozilla上关于递归的这个术语,解释说一个递归函数可以接受两个输入参数:一个最终状态(终止递归)或一个递归状态(继续递归)。)
    // 从两个入参的角度看,newList.length>10000 为真可以理解为“最终状态”。
    // 这个条件设置无关业务,是出于极限保护设置的,可不设。因为假设这个 tree 数据扁平化后不可能超过 100000,
    // 一旦超过说明出错了,所以做出了这个极限保护条件。
    if (newList.length > 100000) {
      return
    }

    tree.map(e => {
      newList.push(e)

      // 自己调用自己,条件是 e.children.length 为真
      // 从两个入参的角度看,e.children.length 为真可以理解为“递归状态”。
      if (e.children && e.children.length) {
        treeTranslate(e.children, newList)
      }
    })
  }

  /**
   * 反扁平化:将扁平数据转换为 tree 数据
   * @param list 扁平数据
   */
  function toTree (list) {
    let tree = []
    list.map(e => {
      // 以 e.pid===null,作为判断是否根节点的依据,或者直接写死根节点(如果确定的话),
      // 具体以什么为根节点的依据得看数据的设计规则,通常是判断层级或是否根节点的标记
      if (e.pid === null) {
        tree.push(e)
      }

      list.map(e2 => {
        if (e2.pid === e.id) {
          // 避免出现重复数据
          const index = e.children.findIndex(sub => sub.id === e2.id)
          if (index === -1) {
            e.children.push(e2)
          }
        }
      })
    })

    console.log("反扁平化后:",tree)
  }
</script>
</body>
</html>

复制代码
分类:
前端
分类:
前端
收藏成功!
已添加到「」, 点击更改