一:首先介绍什么是递归:
递归就是在函数的内部调用自己的函数的形式。
二:递归的思想精髓:
正如上面所描述的场景,递归就是有去(递去)有回(归来),如下图所示。“有去”是指:递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决,就像上面例子中的钥匙可以打开后面所有门上的锁一样;“有回”是指 : 这些问题的演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去。最后,从这个临界点开始,原路返回到原点,原问题解决。
更直接地说,递归的基本思想就是把规模大的问题转化为规模小的相似的子问题来解决。特别地,在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况,这也正是递归的定义所在。格外重要的是,这个解决问题的函数必须有明确的结束条件,否则就会导致无限递归的情况。
三:递归的三要素
在我们了解了递归的基本思想及其数学模型之后,我们如何才能写出一个漂亮的递归程序呢?笔者认为主要是把握好如下三个方面:
1、明确递归终止条件;
2、给出递归终止时的处理办法;
3、提取重复的逻辑,缩小问题规模。
1). 明确递归终止条件
我们知道,递归就是有去有回,既然这样,那么必然应该有一个明确的临界点,程序一旦到达了这个临界点,就不用继续往下递去而是开始实实在在的归来。换句话说,该临界点就是一种简单情境,可以防止无限递归。
2). 给出递归终止时的处理办法
我们刚刚说到,在递归的临界点存在一种简单情境,在这种简单情境下,我们应该直接给出问题的解决方案。一般地,在这种情境下,问题的解决方案是直观的、容易的。
3). 提取重复的逻辑,缩小问题规模*
我们在阐述递归思想内涵时谈到,递归问题必须可以分解为若干个规模较小、与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。从程序实现的角度而言,我们需要抽象出一个干净利落的重复的逻辑,以便使用相同的方式解决子问题。
四。具体实例:
1.斐波那契数列的JS代码实现:
//----------------------------我是分割线-----------------------------------------
//性能优化的问题
var obj = {};
function FibonacciSequence ( n ) {
if(obj[n]) { //如果进入说明这个位已经求过了
return obj[n];
}else if(n == 1 || n ==2) {
//将这个数据也存入对象中
obj[n] = 1;
return 1;
}else {
//将这个数据也存入对象中
obj[n] = FibonacciSequence(n-1) + FibonacciSequence(n-2);
return obj[n];
}
}
//函数的调用
console.log ( FibonacciSequence ( 50 ) );
2.遍历元素的子元素以及遍历Dom树的实现代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="father">
<div></div>
<div></div>
<div>
<p></p>
<p>
<b></b>
<b></b>
</p>
</div>
</div>
<script>
var father = document.getElementById('father')
//遍历dom树的函数
/**
* 作用:遍历元素内部所有的子元素
* @param ele
*/
//定义一个数组来存放遍历出来的元素
var list = [];
function getDomElements ( ele ) {
var childlist = ele.children;
//这里面的递归终止条件隐含是:当元素没有子元素的话,那么childlist.length是0,循环自动执行结束
for(var i = 0;i<childlist.length;i++){
//获取子元素,并将子元素取出来放入数组中
var child = childlist[i];
list.push(child);
//获取子元素的子元素
getDomElements(child);
}
}
//遍历id为father的Div里面的所有的子元素
getDomElements(father);
console.log ( list );
//遍历整个dom树的操作
getDomElements(document);
console.log ( list );
</script>
</body>
</html>
3.遍历元素的子元素以及其对应的ID名与类名的实现代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="father">
<div id="div1" class="div1"></div>
<div id="div2" class="div2"></div>
<div id="div3" class="div3">
<p></p>
<p id="p" class="p">
<b></b>
<b id="b" class="b"></b>
</p>
</div>
</div>
<script>
var father = document.getElementById('father')
//遍历dom树的函数
/**
* 作用:遍历元素内部所有的子元素以及子元素的ID名及类名
* @param ele
*/
//定义一个数组来存放遍历出来的元素以及类名以及ID名
var list = [];
function getDomElements ( ele ) {
var childlist = ele.children;
//这里面的递归终止条件隐含是:当元素没有子元素的话,那么childlist.length是0,循环自动执行结束
for(var i = 0;i<childlist.length;i++){
//获取子元素,并将子元素取出来放入数组中
var child = childlist[i];
var childName = childlist[i].className;
var childID = childlist[i].id;
list.push(child,childName,childID);
//获取子元素的子元素
getDomElements(child);
}
}
//遍历id为father的Div里面的所有的子元素以及类名以及ID名
getDomElements(father);
console.log ( list );
</script>
</body>
</html>
3.1 对应的遍历结果
4.遍历元素的子元素以及对应的文本实现代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="father">
我是fatherdiv
<div id="div1" class="div1">我是第一个div</div>
<div id="div2" class="div2">我是第二个div</div>
<div id="div3" class="div3">
我是第三个div
<p>我是第一个p标签</p>
<p id="p" class="p">
我是第二个p标签
<b>我是第一个b标签</b>
<b id="b" class="b">我是第二个b标签</b>
</p>
</div>
</div>
<script>
var father = document.getElementById('father')
//遍历dom树的函数
/**
* 作用:遍历元素内部所有的子元素以及子元素的文本内容
* @param ele
*/
//定义一个数组来存放遍历出来的元素以及类名以及ID名
var list = [];
function getDomElements ( ele ) {
//获取子元素数组
var eleChild = ele.childNodes;
for(var i=0;i<eleChild.length;i++) {
//拿到具体的子元素
var child = eleChild[i];
//找到子元素的文本
var childtxt = child.innerText;
list.push(childtxt);
getDomElements(child);
}
}
//遍历id为father的Div里面的所有的子元素以及类名以及ID名
getDomElements(father);
console.log ( list );
</script>
</body>
</html>
其中的undefined是因为代码之间有回车的问题,只需要将所有的代码都写成一行,那么就可以去掉undefined了。