递归那些事儿,外祖母的神秘宝箱...

前言

  忽日,走亲访友见祖母,于无聊,上阁楼,见一大箱子。一眼不以为然,再眼,定睛其竟闪闪发光。(批注:母亲大人已经三天没有打我了)

 于是,祖母拿出一大盒子(里面还有很过小盒子,小盒子可能是钥匙也可能是盒子....)

 分析

  只有从这个盒子中找出钥匙,才能打开箱子。然而盒子里面还有盒子,盒子里面还有盒子,也可能是钥匙...于是

方案一:

 1 使用的是while循环:只要盒子堆不空,就从中
 2 取一个盒子,并在其中仔细查找。
 3 def look_for_key(main_box): 
 4   pile = main_box.make_a_pile_to_look_through() 
 5   while pile is not empty: 
 6    box = pile.grab_a_box() 
 7    for item in box: 
 8     if item.is_a_box(): 
 9       pile.append(item) 
10     elif item.is_a_key(): 
11       print "found the key!"

 方案二:

1 使用递归——函数调用自己,这种方法的伪代码如下。
2 def look_for_key(box): 
3   for item in box: 
4    if item.is_a_box(): 
5     look_for_key(item)
6    elif item.is_a_key(): 
7     print "found the key!"
这两种方法的作用相同,但在我看来,第二种方法更清晰。递归只是让解决方案更清晰,并
没有性能上的优势。实际上,在有些情况下,使用第一种方案的性能更好。            

细思极恐

假如,使用递归找到一个盒子,里面既有钥匙又有盒子该怎么办?

 虽然钥匙已经找到了,但程序并没有识别出来,如果有无穷个盒子,那么程序将陷入死循环...

基线条件和递归条件

为解决正上述问题,就必须告诉函数何时结束,正因为如此,每个递归函数都有两部分:基线条件(base case)递归条件(recursive case)。递归条件指的是函数调用自己,而基线条件则
指的是函数不再调用自己,从而避免形成无限循环。改进伪代码如下
 1 使用递归——加上基线条件。
 2  def look_for_key(box): 
 3    for item in box: 
 4     if item.is_a_box() and item.is_a_key():   # 基线条件
 5      print "found the key!"
 6         return
 7     elif item.is_a_key():  # 基线条件
 8      print "found the key!"
 9         return
10     elif item.is_a_box():    # 递归条件
11      look_for_key(item)     
12       else#防止盒子为空
13         print "this box is Null"
14         look_for_key(item.next()) # 继续查找下一个盒子 

剖析递归内部调用情况

在方案一中咱们创建了一个盒子堆,因此咱们始终知道还有哪些盒子为查找

 但使用递归没有盒子堆,如果查找到某个盒子为空,递归函数会不会结束喃?既然没有盒子堆,那算法怎么知道还有哪些盒子需要查找呢?客官别着急,接着往下看...

 ???,C盒子去哪儿了?程序不会结束吧?

不用着急,其实计算机已经为咱们列好了一个调用清单,内容如下

 这个调用清单有一个专业的名词——调用栈,调用栈?

堆栈的自我简述

我的名字叫堆栈,其实就是一种数据结构,因为出生于“社会底层”,所以取名叫堆栈,我还有很多兄弟姐妹(数组,堆栈,队列,链表,树,图,字典树,哈希表),他们性格迥异,这里暂时不提他们。我还有一个小名叫数据篮子,这与我的特性有关,人们总喜欢稀奇古怪的东西往我这里放,所以为你惩戒他们,扔过来的东西,一律先进后出(First In Last Out,FILO),哈哈哈。我还有一个兄弟与我很相似,叫队列,但它总是与我唱反调,别人扔过来的东西,一律先进先出(First In First Out,FIFO),真是气煞我也。我还有很多故事,如果客官有兴趣的话,以后慢慢听我道来...

 小结

使用栈虽然很方便,但是也要付出代价:存储详尽的信息可能占用大量的内存。每个函数调用都要占用一定的内存,如果栈很高,就意味着计算机存储了大量函数调用的信息。在这种情况下,你有两种选择。
1.重新编写代码,转而使用循环。
2.使用尾递归。这是一个高级递归主题,下次讨论。另外,并非所有的语言都支持尾递归。
下一节 采花大盗之狄克斯特拉算法
原文地址:https://www.cnblogs.com/jum-bolg/p/12667550.html