递归
什么是递归
函数递归就是在函数中调用自身
递归的能力在于用有限的语句来定义对象的无线集合
递归需要有边界条件
、递归前进段
和递归返回段
当边界条件不满足时,递归前进
当边界条件满足时,递归返回
function doA(n) {
...
doA(n-1);
}
例子
想象一下,你是一家公司的话务员。
由于公司业务繁忙,你的座机连接多条线路,每条线路上对应一个接收器按钮,当有来电时,该按钮闪烁。
你到达公司开始工作时,发现有四条线对应按钮正在闪烁,你需要接听这些电话。
你接听线路一,并告诉他”请稍等”;然后接听线路二,并告诉他”请稍等”;接着,接听线路三,告诉他”请稍等”;最后接听电话四,并与其通话交谈。
当你结束了线路四的通话后,你回过头来接听线路三,当你结束与线路三通话后,你接听了线路二,最后你接听了线路一
这个例子中每一通电话就像某函数中的一个递归调用。
当你接到一个电话不能立即处理时,这个电话将被搁置;当你有一个不需要立即触发的函数调用时,他将停留在调用栈上。
当你可以接听这个电话时,这个线路会被接通;当你的代码能够触发一个函数调用时,它会从调用栈中弹出
单你看到代码有些发懵时,请回想一下这个比喻
常见递归操作
递归求和(1 + 2 + 3 … + n 的值)
function sum(d) {
if (d <= 1) {
return 1;
} else {
return sum(d - 1) + d;
}
}
/* 执行过程 */
sum(4); //递归前进
4 + sum(3); //递归前进
4 + (3 + sum(2)); //递归前进
4 + (3 + (2 + sum(1))); //递归前进
4 + (3 + (2 + (1 + sum(0)))); //递归返回
4 + (3 + (2 + (1 + 0))); //递归返回
4 + (3 + (2 + 1)); //递归返回
4 + (3 + 3); //递归返回
4 + 6; //递归返回
10; //递归返回
递归实现阶乘函数(1 * 2 * 3 … * n 的值)
function factorial(d) {
if (d <= 1) {
return 1;
} else {
return factorial(d - 1) * d;
}
}
先递进,在回归
斐波那契数列
1,1,2,3,5,8,13……即从第三项起,每一项的值是前两项值的和。求第 n 项的值。
function fibona(n) {
if (n === 1 || n === 2) {
//如果求第一项或者第二项,则直接返回1
return 1;
} else {
//否则的话,返回前两项的和
return fibona(n - 1) + fibona(n - 2);
}
}
console.log(fibona(10));
首先设置终止条件,n=1 或 n=2 时返回 1,否则就重新调用自身
即第 n 项的值是第 n-1 和 n-2 的和,同理可得第 n-1 项的值是 n-1-1 和 n-1-2
通过递归,最终转化成了 f(1)和 f(2)的值相加
递归处理数据
- children 处理为 child
- 添加唯一 id
var tree = {
name: "电脑",
children: [
{
name: "F盘",
children: [
{
name: "照片",
children: [],
},
{
name: "文件",
children: [
{
name: "工作文件",
children: [
{
name: "报告",
children: [],
},
],
},
],
},
],
},
{
name: "E盘",
children: [
{
name: "视频",
children: [
{
name: "js教程",
children: [],
},
],
},
],
},
],
};
let i = 0;
function dg(d) {
if (d.children && d.children.length > 0) {
let child = [];
d.children.map((e, e1) => {
child.push(dg(e));
});
i++;
return {
id: i,
name: d.name,
child,
};
} else {
i++;
return {
name: d.name,
child: [],
id: i,
};
}
}
console.log(dg(tree));
小结
- 递归本质时自己调用自己,在终止条件前会一直递归,递归层级到一定程度,会出现栈溢出二停止递归
- 递归通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
参见