递归
刚开始学递归的时候,总觉得递归太难理解了,为什么这段代码就可以这样执行,为什么我们假设这个函数有这样的功能,他就真的有这个功能,他的临界点到底怎么寻找,循环和递归有什么区别?
什么是递归
递归的定义就是在函数的定义中使用函数自身的方法。
递归就是有去(递去)有回(归来)
英文的Recursion从词源上分析只是"re- (again)" + “curs- (come, happen)” 也就是重复发生,再次重现的意思。
我的理解就是 想去看海,便从家里出发,向着海的方向走,走了10公里,发现没有到,便继续走,20公里还没到,30…40…50…,在走到100公里的时候,终于看到海了,心满意足,便从海边回家,10公里,20公里…到家了!也知道了家到海边有多远
从家里到海边的过程便是递去,到了海边,这就是一个临界点,从海边回到家里,便是归来
回到家,家人问你到底走了多远到了海边,你便可以回答走了多远,
这就是递归有去便有回
递归与循环的区别
递归是把问题看成一个整体,逐级分而解决,而循环是把问题看成是一个个独立的个体
递归其实和循环是非常像的,循环都可以改写成递归,递归未必能改写成循环,这是一个充分不必要的条件。
那么,有了循环,为什么还要用递归呢??在某些情况下(费波纳切数列,汉诺塔),使用递归会比循环简单很多很多
用递归的解决思路
用递归解决问题需要明确几个步骤。
- 递归出口(终止递归的条件)
- 递归表达式(规律)
- 假定我们已经解决了问题,并且有了解决这个问题的函数。
- 如何将问题从 n 的规模降到 n - 1 的规模。
- 递归的出口是什么,也就是当问题规模已经降到最小的时候,我们如何解决它
实例
实例一:下面的代码就是一个简单的递归函数,在这个函数里面调用了自己,会一直循环往复,没有止境,这种叫做死递归
function fn(){
console.log(1)
fn()
}
// fn();//调用fn,执行fn里面的代码,打印1,又执行fn,又打印1,又执行fn,......
案例二:简单实现一个递归
==>需求:求1到5的和
思路 ==>先得到1+2=3
==>再算3+3=6
==>再算6+4=10
==>再算10+5=15
==>结束
//开始书写:写递归函数先要写一个结束条件(为了避免出现"死递归")
function add(n){
function add(n){
//add(n)这个函数的作用是:1到n的累加和
//当n===1的时候就要结束
if(n===1){ //当n等于1时,终止并返回
return 1;
}else{
return add(n-1)+n; //add(n-1)+n可以理解为 (5-1)+5,(4-1)+4,(3-1)+3...
}
}
console.log(add(5));
案例3 算出n-100的阶乘
function factorial(n){
//思路:先想到结束递归的条件,因为是n到100,所以出口就是 n==100;时结束
if( n == 100){
return 100;
}else{ //不满足时
return factorial(n+1)*n
}
}
console.log(factorial(n))
案例4 计算Fibonacci sequence的第N项:
斐波那契数列(Fibonacci sequence),又称黄金分割数列、
因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,
指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,
斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3)
规律就是:前两项之和等于第三项
1 + 1 = 2
2 + 3 = 5
13 + 21 = 34
function fibonacci(n){
if(n==1){
return 1;
}
if(n==2){
return 1;
}
//n = 3 :return fibonacci(2)+fibonacci(1)
//n = 4 :return fibonacci(3)+fibonacci(2)
//由此得出规律
return fibonacci(n-1)+fibonacci(n-2);
}
console.log(fibonacci(6))
理解递归的关键在于两点:1)把问题分解成小部分,用函数写出来,然后一层一层“重新组合”起来2)有一个基本情况即Base case,当到达基本情况时,就会触底,递归调用也就结束了。 例如,类似( ( ( ( () ) ) ) )这种嵌套的括号一样。每一次的行为都是相同的,就是用括号()把内部的东西括起来,一层一层组合起来(RecursiveStep)。但是会有一种BaseCase,那就是(),括号内部为空,这样就会触底,再一层层的从括号中跳出来。即分解了问题,又重组了答案。 所以,在用递归去写函数时,一定要有一个基础情况,放在函数的第一步,然后不是基础情况下递归调用,重复类似操作。
什么时候需要用递归
递归适合用在:
数据的结构形式是按照递归定义的,比如单链表,二叉树,斐波那契数列等;
数据的结构形式不是按照递归定义的,但是用递归求解比用循环求解更加简单,比如汉诺塔问题,四重及以上循环问题。
循环适合用在:
数据的结构形式不是按照递归定义的,使用循环就能够轻松解决的问题,比如一重循环、二重循环、三重循环。
部分资料来源链接:https://www.zhihu.com/question/20507130/answer/29178379;