js递归调用注意return返回值

本文深入探讨了递归算法的核心概念,包括满足递归的条件、递归调用的注意点及应用示例,同时分析了递归算法的潜在缺陷如堆栈溢出和重复计算问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.满足递归的条件

  • 一个问题能够被分解成规模更小的子问题,且该子问题与原问题有同样的解法,将问题一直分解到规模最小的问题后,求解最小问题。
  • 存在一个能让递归调用停止的出口。
  • 先是递进,然后是回归

2.运用递归的注意点

  • 递归调用函数时通过console.log()打印有值,但是在获取的时候是undefined,原因是在递归函数内部没有对递归函数进行return,否则外层函数无法接收到返回值
  • 如果想要函数最后一次计算所得值,就需要在每次调用该函数的时候进行return,每一次return都是把最新的函数调用返回给外层的函数调用。

3.应用示例

  • 数字递归求和,亦可用循环
// 1~num  数字递归求和
function recursionSum(params) {

     let temp = null

     function addNum(num) {

         if (num < 0) {
             return 0
         }

         temp = num

         return 0 + temp + addNum(num - 1)
     }

     return addNum(params)
     
 }

 console.log(recursionSum(6));

4.递归的缺陷

  • 可能堆栈溢出
    递归的过程,可以理解为函数调用栈进出问题。当递归的层次很深时,可能会导致栈溢出
    (1)我们可以规定递归的最大深度,当超过该深度时,报错。下面是斐波那契的例子。
let depth = 0
function fc(num) {
    ++depth
    if(depth > 5) throw new Error('超过最大深度')
    console.log('depth==', depth);
    console.log('num==', num);
    if (num == 1 || num == 2) return 1
    return fc(num - 1) + fc(num - 2)
}

const res = fc(5)
console.log(res);

(2)我们可以使用迭代循环,去改写递归,每次都去计算值,而不是将函数存储在内存中等待调用,从而避免堆栈溢出。

function fc(num) {
    let res = 0;
    let pre = 1;
    let lastPre = 1;
    let i = 3
    for (i; i <= num; i++) {
        res = pre + lastPre
        lastPre = pre
        pre = res
    }
    console.log(res);
}
fc(6)
  • 重复计算度高(这个根据具体逻辑来看,不是所有的递归都有重复计算)
    (1)非重复计算度高栗子-- 数组求和
function add(arr) {
  if(!arr.length) {
      return 0
  }
  console.log(arr[0]);
  return arr[0] + add(arr.slice(1))
}

console.log(add([1,2,3]));

(2)对于重复计算度高的递归
可以使用哈希表(定义:哈希表是一种根据关键码去寻找值的数据映射结构)存储计算过的值。递归过程中,先去哈希表查找有无计算过。

<think>嗯,用户问的是递归什么时候需要返回值,什么时候只要一个return。这个问题看起来是关于递归函数设计的。我得先回忆一下递归的基本概念,然后结合例子来思考。 首先,递归函数通常有两个部分:终止条件和递归调用返回值的存在与否会影响递归的行为。比如,有些递归可能只需要执行某些操作而不需要返回结果,比如遍历树节点但不需要收集数据。这时候可能不需要返回值,或者只需要一个简单的return。 那什么时候需要返回值呢?比如计算阶乘的时候,每一步都需要返回当前的结果给上一层,这样最后才能累积出结果。这时候必须要有返回值。而如果只是打印节点值,可能不需要返回值,函数执行完就自动结束了。 另外,可能用户混淆了是否需要处理返回值和是否需要返回的问题。比如在二叉搜索树中查找某个值,如果找到了就需要立即返回,这时候返回值是必要的。而如果只是遍历所有节点,可能不需要返回值,或者只需要在找到时设置一个标志。 不过,可能有些情况下即使不需要返回值,函数也会返回void,但通过引用参数或者其他方式传递结果。这时候虽然函数没有返回值,但依然达到了目的。但用户的问题可能更关注函数本身的返回值设计。 可能需要分情况讨论:当递归需要将结果传递给上一层调用时,就需要返回值;当递归只是执行操作而不需要传递结果时,可能不需要返回值或者只需要return终止当前分支。 比如,求二叉树的最大深度,需要比较左右子树的深度并返回较大的那个加一,这时候必须返回数值。而如果是前序遍历打印节点,就不需要返回值,只需要在递归调用前后打印即可。 另外,有时候在递归中提前返回的情况,比如在搜索时找到目标就立即返回true,这时候可能需要返回值来判断是否需要继续搜索。例如,查找路径是否存在,一旦找到就逐层返回true,不再继续搜索其他分支。 总结的话,可能需要根据递归的目的来判断:如果需要累积结果或者传递信息给上一层,就需要返回值;如果只是执行副作用(如修改外部变量、打印输出),可能不需要返回值,或者仅用return来结束当前递归分支。</think>递归函数是否需要返回值主要由递归目的决定。掌握以下原则可帮助你合理设计递归函数: **需要返回值的场景**: 1. **需要累积计算结果**(如阶乘、斐波那契数列) ```python def factorial(n): if n == 1: return 1 # 必须返回基础值 return n * factorial(n-1) # 需要传递计算结果 print(factorial(5)) # 输出120 ``` 2. **需要判断条件传递**(如二叉树搜索) ```python def search(root, target): if not root: return False if root.val == target: return True # 找到立即返回 return search(root.left, target) or search(root.right, target) # 需要传递判断结果 ``` 3. **需要逐层传递状态**(如求二叉树深度) ```python def max_depth(root): if not root: return 0 # 必须返回基准值 return 1 + max(max_depth(root.left), max_depth(root.right)) # 需要比较子结果 ``` **不需要返回值的场景**: 1. **纯遍历操作**(如打印全排列) ```python def permute(arr, start=0): if start == len(arr): print(arr) # 直接输出结果 return # 无需返回值 for i in range(start, len(arr)): arr[start], arr[i] = arr[i], arr[start] permute(arr, start+1) arr[start], arr[i] = arr[i], arr[start] ``` 2. **通过引用修改数据**(如反转链表) ```python def reverse_list(node, prev=None): if not node: return # 只需修改指针关系 next_node = node.next node.next = prev reverse_list(next_node, node) # 通过参数传递状态 ``` 3. **提前终止递归分支**(如组合求和) ```python def combination_sum(candidates, target, path=[]): if target < 0: return # 直接终止无效分支 if target == 0: print(path) return for i in range(len(candidates)): combination_sum(candidates[i:], target-candidates[i], path+[candidates[i]])
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值