经典的递归面试题

细胞分裂

细胞分裂 有一个细胞 每一个小时分裂一次,一次分裂一个子细胞,第三个小时后会死亡。那么n个小时候有多少细胞?
这个问题的核心就是 三个小时为一个周期,参考 Javascript中的尾递归及其优化



// 每三个小时为一个周期 , 第四个小时就 go die 了。
// 方法一
// 第一个小时,只有a态细胞;第二个小时,a态细胞分裂,原来的a态细胞变成了b态细胞,分裂出来的细胞变成了新的a态细胞;第三个小时,a态细胞继续分裂变成b态细胞和新的a态细胞,b态细胞分裂变成c态细胞和a态细胞;第四个小时,a、b、c态细胞都会分裂,并且按照之前的规律转变。得出下面的结论
// a 初始态 一个小时 前一个小时的 a+b+c
// b 幼年态 两个小时 前一个小时的 a
// c 成熟态 三个小时 前一个小时的 b
function allCell(n){
    // a态细胞
    let aCell = function(n){
        if(n==1){
            return 1;
        }else{
            return aCell(n-1)+bCell(n-1)+cCell(n-1);
        }
    }
    // b态细胞
    let bCell = function(n){
        if(n==1){
            return 0;
        }else{
            return aCell(n-1);
        }
    }
    // c态细胞
    let cCell = function(n){
        if(n==1||n==2){
            return 0;
        }else{
            return bCell(n-1);
        }
    }
    return aCell(n)+bCell(n)+cCell(n)
}
console.log(allCell(10))
// 方法二
// 这个方法就是分成了 活着的 和 死亡的
function cell(hour){
    // 活着的细胞
    function livecell(hour){
        if(hour<4){
            // 前三个小时没有死亡的细胞 成2的n-1次方增长
            return Math.pow(2,hour-1)
        }else{
            // 从第四个小时开始有死亡的细胞 
            // 活着的细胞 = 前一个小时活着的细胞 - 这个小时死去的细胞
            return livecell(hour-1)*2 - diecell(hour)
        }
    }
    // 死亡的细胞
    function diecell(hour){
        if(hour<4){
            // 前三个小时没有死亡的细胞
            return 0
        }else{
            // 因为三个小时一个周期
            // 也就是每三个小时,(n-3)时的细胞就会死完
            // 那么 这个小时(n)死去的细胞 + 上个小时(n-1)死去的细胞 + 前两个小时(n-2)死去的细胞 = 前三个小时(n-3)活着的细胞
            return livecell(hour-3) - diecell(hour-1) - diecell(hour-2)
        }
    }
    return livecell(hour)
}
console.log(cell(10))

斐波那契数列

又称兔子数列,是以兔子繁殖为例,得出这样一个数列:1,1,2,3,5,8... 指从第三个值开始,每个值都是前两个值的和。



// 递归
    let a = 0;
    function tu(num){
        a++
        if(num==1||num==2){
            return 1
        }
        let nums = tu(num-1)+tu(num-2)
        return nums
    }
    console.log(tu(8),a)
// 闭包解决
    // 也就是存在数组中,再次循环时,如果数组中已经存在,就返回数组中的值,大大减少了递归调用函数的次数
    var count2=0;
    var fiba = (function(){
        var arr = [0,1,1];   //第0位只是占位,从第一位开始算起
        return function(n){
            count2++;   
            var res=arr[n]; 
            if(res){// 如果arr中存在,返回这个值
                console.log(res,'----')
                return res;
            }else{
                console.log(arr[n],'+++++')
                arr[n]=fiba(n-1)+fiba(n-2);
                return arr[n];
            }   
        }
    })();
    console.log(fiba(8),count2)
// 普通
    // 普通循环解决这个问题是性能最好的。
    let a = 1;
    let b = 1
    let c;
    function tu(num){
        for(let i=0;i<num-2;i++){
            c = a+b;
            a = b;
            b = c
        }
        return c;
    }
    console.log(tu(8))

64格子

有 64 个格子,第一个格子放一粒麦子,第二个放2粒,第三个放4粒...每个格子都是前边的两倍。一共有多少粒?



let sum = 0
let start = 1;
let end = 0;
function tow(){
    if(end>=64){
        return false
    }
    sum+=start
    start*=2
    end++
    tow()
}
tow()
console.log(sum)

使用递归实现数组快速排序方法

这个问题的核心思想是 取中间值,然大的放一边,小的放一边,然后再对两边执行一样的操作,也就是递归了。



var mysort = function(arr){
    // 结束条件
    if(arr.length<=1){
        return arr
    }
    // 找基准值 数组中间值
    // 求下标
    var center = Math.floor(arr.length/2)
    var jZ = arr.splice(center,1)[0]
    // 创建两个空数组
    var left = [] , right = [];
    // 循环数组,并进行比较
    for(var i=0;i<arr.length;i++){
        if(arr[i]<jZ){
            left.push(arr[i])
        }else{
            right.push(arr[i])
        }
    }
    return mysort(left).concat([jZ],mysort(right))
}
console.log(mysort([1,6,4,3,7,8,9]))

九九乘法表




// 递归
function num(nums){
    if(nums==1){
        console.log("1x1=1")
    }else{
        num(nums-1)
        for(var i=1,str='';i<=nums;i++){
            str += `${i}x${nums}=`+i*nums+' '
        }
        console.log(str)
    }
}
num(9)
// 循环
for(var i=1;i<10;i++){
    let str = ''
    for(var j=1;j<10;j++){
        if(i>=j){
            str += `${j}x${i}=`+i*j+' '
        }
    }
    console.log(str)
}

来源:https://segmentfault.com/a/1190000018607496

<think>好的,我需要回答用户关于前端面试中递归相关的问题和解答。首先,我得回忆一下用户提供的引用内容,看看是否有相关的信息。引用[2]提到JavaScript中的递归,包括阶乘、斐波那契数列等示例,这应该有用。引用[4]虽然主要讲for in和for of,但可能和遍历数据结构相关,比如树,递归常用于树的操作。 用户需要的是前端面试题中涉及递归的问题,所以应该涵盖常见的递归问题,比如阶乘、斐波那契、深拷贝、DOM遍历等。同时,每个问题需要示例代码和解释,确保符合LaTeX格式要求,行内数学表达式用$...$,独立公式用$$...$$。 首先,整理常见的递归面试题。例如: 1. 实现阶乘函数 2. 斐波那契数列 3. 深拷贝对象 4. 遍历DOM树 5. 数组扁平化 然后,每个问题给出示例代码,用JavaScript,并解释递归的工作原理和注意事项,如基准条件和递归条件。同时,需要确保代码块正确,使用三个反引号包裹,标注语言为javascript。 可能还需要提到递归的优缺点,比如栈溢出风险,尾递归优化等,但引用中没有直接提到,不过引用[2]提到了分治策略,可以结合这一点。 在回答过程中,要注意引用来源,比如引用[2]关于递归在JavaScript中的应用,引用[4]可能涉及遍历方法,但可能关联不大,所以主要参考引用[2]。 最后生成相关问题,可能包括优化递归的方法、递归的应用场景、递归与迭代的区别等。确保问题与前端相关,比如如何处理递归导致的栈溢出,或者在实际项目中使用递归的例子。 现在开始组织内容,确保结构清晰,分点回答,每个问题有示例代码和解释,正确使用引用标注,并生成相关问题。</think>### 前端面试中的递归问题与解答 #### 1. 实现阶乘函数 阶乘是递归经典案例。数学定义: $$n! = n \times (n-1)! \quad (n \geq 1)$$ 其中基准条件为 $0! = 1$。 **示例代码**: ```javascript function factorial(n) { if (n === 0) return 1; // 基准条件 return n * factorial(n - 1); // 递归条件 } ``` **解释**:递归逐层分解问题,直到达到基准条件 $n=0$,再逐层返回计算结果[^2]。 --- #### 2. 斐波那契数列 斐波那契数列定义为: $$F(n) = F(n-1) + F(n-2) \quad (n \geq 2)$$ 基准条件为 $F(0)=0$, $F(1)=1$。 **示例代码**: ```javascript function fibonacci(n) { if (n <= 1) return n; // 基准条件 return fibonacci(n - 1) + fibonacci(n - 2); } ``` **注意**:此实现效率较低(时间复杂度 $O(2^n)$),实际面试中需讨论优化方法(如记忆化递归)[^2]。 --- #### 3. 深拷贝对象 递归可用于遍历对象的嵌套属性,实现深拷贝: ```javascript function deepClone(obj) { if (typeof obj !== 'object' || obj === null) return obj; // 基准条件 const clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { clone[key] = deepClone(obj[key]); // 递归拷贝属性 } return clone; } ``` **解释**:通过递归处理嵌套对象/数组,确保每一层数据独立复制[^4]。 --- #### 4. 遍历DOM树 递归适合处理树形结构,如遍历DOM节点: ```javascript function traverseDOM(node) { console.log(node); for (let child of node.children) { // 使用for of遍历子节点 traverseDOM(child); // 递归处理子节点 } } ``` **说明**:递归逐层访问子节点,直到叶子节点(无子节点的节点)终止[^4]。 --- #### 5. 数组扁平化 递归可将多维数组转换为一维数组: ```javascript function flatten(arr) { return arr.reduce((acc, item) => { return acc.concat(Array.isArray(item) ? flatten(item) : item); }, []); } ``` **逻辑**:递归检查数组元素,若为数组则继续展开,否则直接拼接[^2]。 --- ### 递归的核心要点 1. **基准条件**:明确递归终止条件,否则会导致栈溢出。 2. **递归条件**:问题规模需逐步缩小,向基准条件逼近。 3. **分治策略**:将问题分解为更小的子问题(如树的遍历、归并排序)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值