JS学习之递归和常见的几种递归算法

递归的概念

递归是一种用于解决计算机问题的技术,方法是创建一个调用自身的函数,直到程序达到预期的结果。

❗️注释:就是函数自己调用自己本身,或者在自己函数调用的下级函数中调用自己。

如何读取递归函数

乍一看,递归函数并不直观或易于理解。以下步骤将帮助您更快地阅读和理解递归函数:

  • 始终先确定函数的基本情况。
  • 将参数传递给将立即到达基本情况的函数。
  • 确定至少将执行递归函数调用一次的参数。

使用上面的示例尝试读下面的代码randomUntilFive()。

function randomUntilFive(result = 0, count = 0){
    if(result === 5){
        // 触发结束
    }
    // 递归调用函数
}
randomUntilFive();

这意味着可以通过将数字传递给5参数来达到结束。

function randomUntilFive(result = 0, count = 0){
    if(result === 5){
        console.log(`随机结果: ${result}`);
        console.log(`随机执行次数: ${count}`);
        return;
    }
}
randomUntilFive(5);

虽然count参数不应该为零,但将数字5作为参数传递给上面的函数调用可以满足第二步的要求。

最后,需要找到一个至少执行一次递归函数调用的参数。在上述情况下,可以传递任何数字,而不是传递任何数字5:

function randomUntilFive(result = 0, count = 0){
    if(result === 5){
        console.log(`随机结果: ${result}`);
        console.log(`随机执行次数: ${count}`);
        return;
    }
    result = Math.floor(Math.random() * (10 - 1 + 1) + 1);
    count++;
    randomUntilFive(result, count);
}
randomUntilFive(4); 
// 5以外的任何数字
// 执行递归调用

如何编写递归函数

编写递归函数与阅读递归函数几乎相同:

  • 使用通过其参数达到的结束条件创建一个常规函数。
  • 将参数传递给立即触发结束条件的函数。
  • 只传递一次触发递归调用的下一个参数。

假设编写一个计算阶乘的函数。下面是五的阶乘:

5*4*3*2*1 = 120

首先,这个函数的结束条件是 1,创建一个factorial返回 1 的函数:

function factorial(num){
    if(num === 1){
        return num;
    }
}
console.log(factorial(1));

现在进入第三步。需要在函数中得到一个递归调用,并且至少调用一次。由于阶乘计算在每次乘法时将数字减一,因此可以通过传递num-1给递归调用来模拟它:

function factorial(num){
    if(num === 1){
        return num;
    }
    return num * factorial(num-1) 
}
console.log(factorial(2));

经典案例 1: 求和

求 1-100 的和

function sum(n) {
  if (n == 1) return 1
  return sum(n - 1) + n
}

经典案例 2: 斐波拉契数列

1,1,2,3,5,8,13,21,34,55,89...求第 n 项

// 递归方法
function fib(n) {
  if (n === 1 || n === 2) return n - 1
  return fib(n - 1) + fib(n - 2)
}
console.log(fib(10)) // 34

//非递归方法 //
function fib(n) {
  let a = 0
  let b = 1
  let c = a + b
  for (let i = 3; i < n; i++) {
    a = b
    b = c
    c = a + b
  }
  return c
}
console.log(fib(10)) // 34

经典案例 3: 爬楼梯

JS 递归 假如楼梯有 n 个台阶,每次可以走 1 个或 2 个台阶,请问走完这 n 个台阶有几种走法

function climbStairs(n) {
  if (n == 1) return 1
  if (n == 2) return 2
  return climbStairs(n - 1) + climbStairs(n - 2)
}

经典案例 4: 深拷贝

原理: clone(o) = new Object; 返回一个对象

function clone(o) {
  var temp = {}
  for (var key in o) {
    if (typeof o[key] == 'object') {
      temp[key] = clone(o[key])
    } else {
      temp[key] = o[key]
    }
  }
  return temp
}

经典案例 5:递归组件

  • 递归组件: 组件在它的模板内可以递归的调用自己,只要给组件设置 name 组件就可以了。
  • 不过需要注意的是,必须给一个条件来限制数量,否则会抛出错误: max stack size exceeded
  • 组件递归用来开发一些具体有未知层级关系的独立组件。比如:联级选择器和树形控件
<template>
  <div v-for="(item,index) in treeArr"> {{index}} <br/>
      <tree :item="item.arr" v-if="item.flag"></tree>
  </div>
</template>
<script>
export default {
  // 必须定义name,组件内部才能递归调用
  name: 'tree',
  data(){
    return {}
  },
  // 接收外部传入的值
  props: {
     item: {
      type:Array,
      default: ()=>[]
    }
  }
}
</script>

posted on 2024-01-29 09:49  梁飞宇  阅读(47)  评论(0)    收藏  举报