Skip to content

Latest commit

 

History

History
111 lines (99 loc) · 5.5 KB

File metadata and controls

111 lines (99 loc) · 5.5 KB

复习题4.7

  1. 递归和迭代术语的定义是什么?一个函数可以同时采用这两种方法吗?

    • 递归:将一个大的问题分解成比较小的、有着相同形式的问题。
    • 迭代:重复执行一项任务。
  2. 递归和逐步求精法的根本区别是什么?

    • 递归所分解出的问题具有相同的形式,而逐步求精分解则不一定具备这个特性。
  3. 再CollectContributions函数的伪码中,为什么使用 if (n<=100)替代对n是否等于100的核算,这种核算为什么重要?

    • 因为筹集的款项并不只是100美元的倍数,分解下来后,可能会出现每个人筹集款项少于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.
    }
  5. 要让递归成为一个解法,问题所必须具备的两个特征是什么?

    1. 必须能够鉴别出一个简单情景,且该情景的答案是容易确定的。
    2. 必须能够确定一个递归的分解方式,能够将问题的复杂实例分解为更小的、具有相同形式的问题。
  6. 为什么说分而治之是对递归技术最恰当的描述?

    • 分治就是将大问题被逐步分解为多个小问题,解决小问题就解决了大问题;递归的解法也是将大问题分解成模式相同的小问题,在最简单的情况下解决。
  7. 对递归跳跃的信任是什么意思?为什么这个概念对程序员来说是很重要的?

    • 抛开底层细节,在一个层次上思考,假设其它层次都已经做正确了;这个信任是递归调用能成立的基础,否则程序就会陷入无尽的细节之中。
  8. 在4.2.2小节“追踪递归过程”中,对调用Fact(4)时程序如何运行进行了大篇幅的分析。把这种分析作为一个模型,追踪Fib(4)的执行过程,画出进程中的每一个栈帧的略图。

    • 略。
  9. 引入成对的兔子在生下3胎后停止繁殖这个规律,修改斐波那契的兔子问题,这种假设时如何改变递归关系的?在简单情景中,您需要做的改变是什么?

    • 递推数列变为:1 1 2 3 5 7 11 16 24 35 52 76 112...
    • 根据每对兔子第二个月发育成熟,第三个月生第一对,最多生3对的关系推测出数列改编为 a(n)= a(n-1) + a(n-2) - a(n-5), 当n<=5时最后一项为0
    • 所以我们需要改变递推式,并且根据情况改变最后一项的值。
  10. 在图4-1给出的递归实现中,当计算Fib(n)时调用了多少次Fib(1)?

    • 5次
  11. 如果从函数AdditiveSequence中将if(n==1)删除,程序会发生什么事情?该函数还能正常工作吗?为什么?

    static int AdditiveSequence(int n, int t0, int t1)
    {
        if (n == 0)
        {
            return t0;
        }
        else
        {
            return AdditiveSequence(n - 1, t1, t0 + t1);
        }
    }
    • 程序依旧可以正确运行,只是在微观层面,此函数会被多调用一次。因为实际上即使不能在n==1的情况下返回,这时的t0 + t1会作为下一次的t0值调用,最后n == 0时返回t0即可。
  12. 什么是包装器函数?为什么它们在写递归程序时用处很大?

    • 包装器函数就是一个直接返回其它函数调用的函数,它可能会额外传递一些参数,在最上层调用时,无需关心额外参数,帮助我们减少复杂度,更直观地理解问题。
    • 被包装的函数通常可以解决更加普遍的问题。
  13. 在图4-3的IsPalindrome视线中,为什么对空字符串的校验和对单字符的校验同样重要?如果不对单字符情景进行校验,而仅仅对长度为0的情形进行校验,将会发生什么?这个函数还能够正确运行吗?

    • 因为存在双数字符的回文,每次调用减少2的字符长度,存在到最后为空串的校验;
    • 如果不检测单字符的情况,会出现调用为负数的情况,这不符合实际情况,程序将发生错误。
  14. 解释图4-4中给出的函数IsPalindrome实现中函数调用CheckPalindrome(str+1, len-2)的作用。

    • 使用指针忽略已经处理的字符串的第一个字符,并缩小问题的规模。
  15. 什么是交互递归?

    • 交互递归是更加深层次的递归调用,f调用g,g再调用f。
  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 TRUE;
        }
        else
        {
            return IsOdd(n - 1);
        }
    }
    
    bool IsOdd(unsigned n)
    {
        if (n == 1)
        {
            return TRUE;
        }
        else
        {
            return IsEven(n - 1);
        }
    }
    • 例如,调用 IsOdd(4),程序将永无终止地运行,直到栈溢出。运行到n == 1时,程序在IsEven不具备退出条件。这个问题属于没有正确解决简单情形。