在Scheme中插入二叉树

时间:2013-10-15 19:00:12

标签: recursion scheme binary-tree

我想知道如何将列表中的元素插入到二叉搜索树中。我想知道为什么下面的代码不能像我预期的那样工作。输出是'((4 1 5 13 6)()())我的下一个问题是要对列表中的元素进行排序,但是现在我只想插入它们。

我说的输出是否正确?我的代码如下:

( define ( make-tree value left right ) 
( list value left right ))
( define ( value tree ) ( car tree ))
( define ( left tree ) ( cadr tree ))
( define ( right tree ) ( caddr tree ))

(define (insert-list L T)
 (cond ((null? T) (make-tree L '() '()))
    ((= (car L) (value T)) T)
    ((< (car L) (value T)) (make-tree (value T)
                                      (insert-list (car L) (left T))
                                      (right T)))
    ((> (car L) (value T)) (make-tree (value T)
                                      (left T)
                                      (insert-list (car L) (right T))))))

1 个答案:

答案 0 :(得分:2)

此代码的问题

在编写递归代码时,通常最好考虑函数应该作为参数使用什么,它应该返回什么,以及基本情况是什么。

(define (insert-list L T)
 (cond
    ((null? T) (make-tree L '() '()))                                     ; case 1
    ((= (car L) (value T)) T)                                             ; case 2
    ((< (car L) (value T)) (make-tree (value T)                           ; case 3
                                      (insert-list (car L) (left T))
                                      (right T)))
    ((> (car L) (value T)) (make-tree (value T)                           ; case 4
                                      (left T)
                                      (insert-list (car L) (right T))))))

根据您的描述,insert-list应该采用元素和树的列表,并返回您从一个接一个地插入这些元素到树中的树。这段代码实际上是这样做的吗?有几种情况:

  1. 当树为空时,您需要创建一个包含某个元素的新树,但是您的第一个案例会创建一个以整个列表作为其元素的树,并返回该值。这就是为什么你得到像((4 1 5 13 6) () ())这样的结果的原因。你得到了这个基本案例并返回了(make-tree L '() '()))
  2. 的结果
  3. 如果列表的第一个元素是树的值,那么您返回树是正确的,因为您不需要执行任何其他操作来插入列表的第一个元素。但是,你不会对其他元素做任何其他事情。这没有任何好处。如果您有(insert-list '(2 3 4) '(2 () ()))之类的测试用例,则会看到返回值为(2 () ())
  4. (和4.)在这些情况下,你进行递归调用,如(insert-list (car L) (left T)),但这没有意义,因为insert-list的第一个参数应该是一个元素列表,并且您使用(car L)来调用它,这是一个单独的元素。但是,你认识到(car L)需要插入到树的左子树中,而你正在构建它,如果只是你正在调用(insert-element (car L) (left T)),那么你是对的。但是,之后您仍然没有对元素的 rest 做任何事情。
  5. 折叠救援!

    如果您正在尝试获取数字列表并将第一个插入树中以获取新树,则将第二个数字插入该树中,依此类推,即可重新寻找像这样的伪代码:

    tree = initial-tree
    for element in list 
      tree = insert(element,tree)
    return tree
    

    这种操作通常由fold在功能上表示。我在Flattening a List of Lists的答案中详细描述了折叠,并且有很多关于它们的信息。想法是你想要转变

    (insert-list '(4 1 5 13 6) '())
    

    进入

    (tree-insert (tree-insert (tree-insert (tree-insert (tree-insert '() 4) 1) 5) 13) 6)
    

    这是左关联折叠。我们使用foldl

    的定义
    (define (foldl fn init list)
      (if (null? list)
          init
          (foldl fn (fn init (car list)) (cdr list))))
    

    在这种特殊情况下,你需要实现你的普通tree-insert函数,它接受一个树,一个元素返回一个新树,然后你的函数插入 all 列表中的元素只是

    (lambda (tree elements)
      (foldl tree-insert tree elements))
    

    例如,

    > (foldl tree-insert '() '(3 5 8 1 9))
    (3 (1 () ()) (5 () (8 () (9 () ()))))
    > (foldl tree-insert '() '(5 8 2 3 1 6 9))
    (5 (2 (1 () ()) (3 () ())) (8 (6 () ()) (9 () ())))
    > (foldl tree-insert '() '(4 1 5 13 6))             ; the test from the question
    (4 (1 () ()) (5 () (13 (6 () ()) ())))