SICP 2.64递归程序的增长顺序

时间:2014-12-04 13:09:11

标签: recursion tree scheme sicp

我是SICP的自学者,很难找到递归函数增长的顺序。

以下过程list->树将有序列表转换为平衡搜索树:

(define (list->tree elements) 
  (car (partial-tree elements (length elements)))) 

(define (partial-tree elts n) 
  (if (= n 0) 
      (cons '() elts) 
      (let ((left-size (quotient (- n 1) 2))) 
        (let ((left-result (partial-tree elts left-size))) 
          (let ((left-tree (car left-result)) 
                (non-left-elts (cdr left-result)) 
                (right-size (- n (+ left-size 1)))) 
            (let ((this-entry (car non-left-elts)) 
                  (right-result (partial-tree (cdr non-left-elts) 
                                              right-size))) 
              (let ((right-tree (car right-result)) 
                    (remaining-elts (cdr right-result))) 
                (cons (make-tree this-entry left-tree right-tree) 
                      remaining-elts))))))))

我一直在网上查看解决方案,我认为以下网站提供了最佳解决方案,但我无法理解它:

jots-jottings.blogspot.com/2011/12/sicp-exercise-264-constructing-balanced.html

我的理解是,程序'partial-tree'每次被调用时都会重复调用三个参数 - 'this-entry','left-tree'和'right-tree'。 (并且只有在必要时才进行'剩余的' - 在第一次“部分树”呼叫或任何“非左撇子”呼叫时)

  1. 此条目调用:car,cdr和cdr(left-result)
  2. 左进入呼叫:car,cdr,以及每一步长度减半的
  3. 右键调用:car,本身用cdr(cdr(左结果))作为参数,长度减半
  4. 'left-entry'将有2个log(n)步,并且所有三个参数分别调用'left-entry'。 因此它将具有三元树状结构,并且我认为步骤的总数将类似于3 ^ log(n)。但解决方案说它只使用每个索引1..n只有一次。但是,例如,“this-entry”是否会将每个节点的相同索引减少为“右键”?

    我很困惑.. 此外,在(a)部分,解决方案网站指出:

      

    “在非终止的情况下,部分树首先计算数字   应该进入平衡二进制文件的左子树的元素   大小为n的树,然后使用元素调用部分树   两者都产生这样一个子树而不产生元素列表的值   在那个子树里。然后它将未使用元素的头部作为   当前节点的值“

    我相信程序在左树之前执行此操作。为什么我错了?

    这是我关于CS的第一本书,我还没有遇到过Master Theorem。 在一些解决方案中提到了它,但希望我能够在不使用它的情况下解决问题。

    感谢您阅读,我期待您的回复,

    克里斯

1 个答案:

答案 0 :(得分:0)

您需要了解let表单的工作原理。在

          (let ((left-tree (car left-result)) 
                (non-left-elts (cdr left-result))

left-tree "来电"任何东西。它被创建为一个新的词法变量,并赋值为(car left-result)。它周围的括号只是组合描述由let形式引入的一个变量的元素:变量的名称及其值:

          (let (  (   left-tree      (car left-result)  ) 
          ;;      ^^                                   ^^
                  (   non-left-elts  (cdr left-result)  )
          ;;      ^^                                   ^^

以下是如何理解 递归程序的工作原理:不要

不要试图了解 的工作方式;而是分析它的作用,假设它(对于较小的情况)它应该做什么。

此处,(partial-tree elts n) 接收 两个参数:元素列表(可能会放入树中)和列表的长度。 返回

            (cons (make-tree this-entry left-tree right-tree) 
                  remaining-elts)

一个树的缺点 - 转换的结果,以及剩下的元素,如果长度参数是正确的,那么它们应该是没有留下的,在最顶层的调用中。

既然我们知道它应该做什么,我们就会深入了解它。确实假设上述内容完全有意义:将元素数量减半,处理列表,返回树和剩余列表(现在非空),然后处理剩下的内容。

this-entry 不是树 - 它是一个位于树节点中的元素:

            (let ((this-entry (car non-left-elts)) 

设置

                  (right-size (- n (+ left-size 1))

表示n == right-size + 1 + left-size。进入节点本身的那个元素是this-entry元素。

由于每个元素直接进入其节点,因此该算法的总运行时间与输入列表中的元素数呈线性关系,并使用对数堆栈空间。