递归算法

Posted 。。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归算法相关的知识,希望对你有一定的参考价值。

什么是递归?

程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

注意:递归是有出口的,没有出口就会造成内存溢出。

给你讲一个故事就明白了,什么故事呢?

  从前有座山,山里有个庙,庙里有个老和尚在给小和尚讲故事,讲的是从前有座山,山里有个庙,庙里有个老和尚在给小和尚讲故事,讲的是从前有座山...

这时候必须需要给讲故事的人限定一个结束值,比如第十次提到小和尚时结束该故事(否则就是一个死递归),这就是一个典型的递归。

JS例子:

  1. 有一组多级嵌套数据,用于生成一个树形组件,数据包含label,id,children属性,每一个数据id值都不同,这时已知一个固定id,需要查找出这个id对应的label值,就可以用到递归。

    例如:后台返回以下数据结构:

    [{
      label: \'一级 1\',
      id: 1,
      children: [{
        label: \'二级 1-1\',
        id: 2,
        children: [{
          id: 3,
          label: \'三级 1-1-1\'
        }]
      }]
    }, {
      label: \'一级 2\',
      id: 4,
      children: [{
        label: \'二级 2-1\',
        id: 5,
        children: [{
          id: 6,
          label: \'三级 2-1-1\'
        }]
     }, {
        label: \'二级 2-2\',
        id: 7,
        children: [{
          id: 8,
          label: \'三级 2-2-1\'
        }]
     }]
    }, {
      label: \'一级 3\',
      id: 9,
      children: [{
        label: \'二级 3-1\',
        id: 10,
        children: [{
          id: 11,
          label: \'三级 3-1-1\'
        }]
     }, {
        label: \'二级 3-2\',
        id: 12,
        children: [{
          id: 13,
          label: \'三级 3-2-1\'
        }]
      }]
    }]

前端需要获取id=12时的label,这时你会怎么做?

const data = 该数据
let label = \'\'
const recursionFn = (currentData) => {
  currentData.forEach(item => {
    if (item.id === 12) { 
      label = item.label
      return
    }
    item.children && recursionFn(item.children)
  })
}

recursionFn(data)
console.log(label)
  1. 进阶: 合并对象:

    有两个对象,其中

    a对象为:

     const a = {
      name: \'a\', 
      children: false,
      color: \'#ccc\',
      attribute: {
        label: \'测试\',
        text: \'age\',
        attributes: {
          js: false
        }
      }
    }
    
    const b = {
      name: \'b\', 
      children: true,
      age: 30,
      color: \'#ccc\',
      attribute: {
        label: \'默认\',
        text: \'默认\',
        attributes: {
          js: true,
          json: false
        }
      }
    }

    此时需要将a和b的属性以及其下面的所有属性进行合并,得到的对象必须含有a和b的所有属性,但如果该属性在a中已有时,以a已有的属性为主。

   const mergeObjects = (obj, otherObj, valueKey = null) => {
   for (const key in otherObj) {
     if (otherObj[key] !== null && (typeof otherObj[key] === \'object\')) {
       obj[key] = {
         ...otherObj[key],
         ...obj[key]
       }
       const keys = valueKey ? `${valueKey}.${key}` : (valueKey === null ? null : key)
       mergeObjects(obj[key], otherObj[key], keys)
     } else {
       const objKey = valueKey || key
       obj[objKey] = obj[objKey] !== undefined ? obj[objKey] : otherObj[objKey]
     }
   }
   return obj
 }
 const mergeObj = mergeObjects(a, b)
  1. 递归实现深拷贝:

    深拷贝在日常开发中经常被用到,知道如何实现吗?

    const cloneDeep = (obj) => {
      const temp = {}
      for (var key in obj) {
        if (typeof obj[key] == \'object\') {
          temp[key] = clone(obj[key])
        } else {
          temp[key] = obj[key]
        }
      }
      return temp
    }
  2. 递归实现阶乘:

    阶乘: 一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。

    任何大于等于1 的自然数n 阶乘表示方法:

    n! = 1 2 3 ... (n - 1) * n

      const factorial = (num) => {
        return num < 0 ? -1 : (num === 0 || num === 1 ? 1 : (num * factorial(num - 1)))
      }
      factorial(3)
  3. 递归实现斐波拉契数列:

    斐波拉契数列:数列从第3项开始,每一项都等于前两项之和。

    如: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144.....

    求其中某一项的值:

      const fib = (num) => {
        if (n === 1 || n === 2) return n - 1
        return fib(num - 1) + fib(num - 2)
      }
  4. 递归实现树结构:

    现有一组数据:

    
      [
        {
            "area_id": 5,
            "name": "广东省",
            "parent_id": 0,
        },  
        {
            "area_id": 6,
            "name": "广州市",
            "parent_id": 5,
        },
        {
            "area_id": 7,
            "name": "深圳市",
            "parent_id": 5,
        },
        {
            "area_id": 4,
            "name": "北京市",
            "parent_id": 3,
        },
        {
            "area_id": 3,
            "name": "北京",
            "parent_id": 0,
        },
        {
            "area_id": 2,
            "name": "测试子地区",
            "parent_id": 1,
        },
        {
            "area_id": 1,
            "name": "测试地区",
            "parent_id": 0,
        }
    ]
这种一级的数据结构怎么转化为无限极的属性结构呢? 

```
  const toTreeData = (data, pid = 0) => {
    let arr = []
    data.filter(item => {
        return item.parent_id === pid;
    }).forEach(item => {
        arr.push({
            area_id: item.area_id,
            label: item.name,
            children: toTreeData(data, item.area_id)
        })
    })
    return arr
  }
```





以上是关于递归算法的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript - 代码片段,Snippets,Gist

以下代码片段的算法复杂度

有人可以解释啥是 SVN 平分算法吗?理论上和通过代码片段[重复]

片段(Java) | 机试题+算法思路+考点+代码解析 2023

CSP核心代码片段记录

算法漫游指北(第十篇):泛型递归递归代码模板递归思维要点分治算法回溯算法