1. 递归和迭代术语的定义是什么?一个函数可以同时采用这两种方法吗?
答: 递归的定义:将一个大问题分解成比较小的、有着相同形式的问题。
迭代跟递归策略是相反的。它使用循环(一般情况下,使用for和while语句)的实现被称为迭代的。他们能够通过截然不同的方式来解决一个问题。然而,这些策略并不互相排斥。
2. 递归和逐步求精法的根本区别是什么?
答:“相同形式”是这个定义中的关键,否则就成了——逐步求精策略,两种策略都涉及到分解。递归的特殊之处就在于子问题有着和初始问题相同的形式。
3. 在CollectContributions函数的伪代码中,if语句如下所示:
if (n <= 100)
使用<=运算符代替简单地核对n是否等于100,为什么很重要?
答:因为这行代码只是将初始问题在一个更小范围内再现。这个任务的最基本的特征——募集n美圆一直保持着。如果大于100美圆,那么函数一直调用自身。这个调用自身的函数,这是递归的决定性特征。
4. 什么是标准的递归范式?
If(test for simple case){
Compute a simple solution without using recursion.
}else{
Break the problem down into subproblems of the same form.
Solve each of the subproblems by calling this function recursively.
Reassemble the solutions to the subproblems into a solution for the whole.
}
该程序结构提供了一个写递归函数的模板,因此被称为递归范式。只要符合如下条件,就可以在编程技术中应用这一技术。
(1) 必须能够鉴别出一个简单情景,且该情景的答案是容易确定的。
(2) 必须能够确认出一个递归的分解方式,能够将问题的复杂实例分解为更小的、具有相同形式的问题。
5. 要让递归成为一个解决,问题必须具备的两个特性是什么?
(1)必须能够鉴别出一个简单情景,且该情景的答案是容易确定的。
(2)必须能够确认出一个递归的分解方式,能够将问题的复杂实例分解为更小的、具有相同形式的问题。
6. 为什么说分而治之是递归技术最恰当的描述?
答:分而治之是递归技术的最恰当的体现,因为这句话说明了递归每个分解问题都需要分别对待,不能看做一个整体。不然会很混乱。
7. 对递归跳跃的信任是什么意思?为什么这个感念对程序员来说是很重要的?
答:当尝试理解递归程序时,必须能够抛开底层的细节,将注意力集中在单个计算层次上。在这个层次上,只要一个递归调用的参数在某些方面能比前一个蚕食更简单,那么就可以认为任何递归调用都能够自动地得到正确的答案。这种心理策略——假设任何更简单的递归都能正确地实现——叫做对递归跳跃的信任。在实际应用中,学习应用这个策略是使用递归的基础,所以对程序员来说很重要!
8. 在4.2.2小节 “追踪递归过程”中,对调用Fact(4)时程序如何运行进行了大篇幅的分析。把这种分析作为一个模型,追踪Fib(4)的执行过程,划出进程中的每一个栈侦的略图。
答:首先主函数main调用Fact时,计算机创建一个新的栈桢并将参数数值复制到形参n。Fact的栈桢临时替代了main的栈桢
在该图中,Fact的主体代码在侦内给出,这样就更容易跟踪这个程序运行的当前位置。该位置在图中用箭头标示出。在当前的图中,箭头出现在代码的开头,因为所有的函数调用都是从函数体的第一条语句开始。
现在,计算机开始执行函数体,从if语句开始。因为n不等于0,所以继续执行到else子句,在该句中,程序必须计算和返回下面表达式的值: n*Fact( n – 1 )
计算上面的表达式要求首先计算这个调用的结果和n相乘。当前的结果用下图所示:
只要对Fact(n-1)的调用返回,那么结果将代替图中有下划线的表达式,然后继续执行下去。
下一侦应该是:
现在有2个侦都标记为Fact。在最近的这个侦中,计算机开始计算Fact(3)。在那先前的哪个被最近新创建的侦所掩盖的侦中,Fact函数等待着对Fact(1)调用的返回值。
然而,必须执行当前的计算完成最高层侦。又一次判断,n不是0,所以控制流程转向if语句的else子句,在该语句中计算机必须计算出Fact(1)。然而,在这个侦中n的值为3,所以要求的结果是通过调用Fact(2)来计算。同前面一样,这一流程要求创建一个新的栈侦,如下所示:
同样的逻辑,现在程序必须调用Fact(1),而它又接着调用Fact(0),因此创建了两个新的栈侦。最终的结果侦结构如下所示:
然而在这一计算位置上,情况有所改变。因为n的值为0,所以函数能够通过执行下列语句,立即返回结果: return(1); 值1返回到调用者侦,该侦重新回到栈顶,如下所示:
从这一点,计算过程一层层返回递归调用,在每一层上完成返回值的计算。该侦中对Fact(n * 1); n的值为1, 所以这个递归调用的结果为1。这个结果被返回给它的调用者,该调用者可以使用如下侦表示:
因为n的值现在是2,因此执行return语句使值2传回到前一个侦中,如下所示:
在这个阶段上,程序把3X2的结果返回到前一个侦中,这时初始对Fact的调用侦变成了如下的形式:
计算过程的最后一步由计算4Ⅹ6并且把24返回到主程序中两部分组成。
9. 引用成对的兔子在生下3胎后停止繁育这个规则,修改费波那契的兔子问题,这种假设是如何改变递归关系的?在简单情景中,您需要做的改变是什么?
答:这种假设把生育3次的兔子除去,这样我们可以再设一递归,只计算不生育的兔子,然后用以前那个总数减去现在不生育的这个就是现在生育的兔子个数。
10. 在图4-1给出的递归实现中,但计算Fid(n)时调用了多少次Fid(1)?
答:调用了5次Fid(1)。
11. 如果从函数AdditiveSequence中将if(n==1)删除,程序如下:
static int AdditiveSequence(int n, int t0, int t1)
{
if ( n == 0) return (t0);
return (AdditiveSequence(n – 1, t1, t0 + t1));
}
将会发生什么事情?该函数还能工作吗?为什么?
答:就会只返回0别的不运算。该函数不能工作了,因为这个递归需要3个参数才能进行运算,少一个参数就不能进行递归运算了。
12. 什么是包装器函数?为什么它们在写递归程序时用处很大?
13. 在图4-3的IsPalindrome实现中,为什么对空字符串的校验和对单字符的校验同样重要?如果不对单字符情景进行校验,而是仅仅对长度为0的情形进行校验,将会发生什么?这个还能够正确运行吗?
答:这样不能正确显示所有结果,像noon形式的可以正确显示,但是level形的就显示不了。
所以就会产生片面的情况。
14. 解释图4-4中给出的函数IsPalindrome实现中函数调用CheckPalindrome(str+1, len-2)的作用.
答:在C语言中,数组总是被当作指定它的第一个元素的指针。如果给其地址加1,那么按照C语言的规则会返回一个在第一个元素之后的元素地址。因此调用这个函数会使指针开始于第2个字符,而比str原始值早结束一个字符。
15. 什么是交互递归?
答:如果函数f调用函数g,而函数g又反过来调用函数f,这些函数的调用仍然被看作是递归的。因为函数f和g互相调用,这种类型的递归被称为交互递归。
16. 如果按下面的方式定义了IsEven和IsOdd:
bool IsEven(unsigned n)
{
return(!IsOdd(n));
}
bool IsOdd (unsigned n)
{
return (!IsEven(n));
}
将会发生什么?
答:会产生无限递归。
17.下面对IsEven和 IsOdd的定义也是不正确的:
bool IsEven(unsigned n)
{
if( n == 0){
return(TURE);
}else{
return(IsOdd(n – 1));
}
}
bool IsOdd(Unsigned n)
{
if (n == 1){
return (TURE); }else{
return (IsEven( n – 1 ));
}
}
给出一个演示这个实现是如何失败的示例.在这里示范的是什么常见错误?
这个只能判断0和1,别的无法判断。
这个示范的是弄错了简单的情景定义,实现策略时出现问题。