Scheme - 恰好有一个 child 的二叉搜索树的内部节点数

Scheme - Number of internal nodes of a binary search tree which have exactly one child

正如标题所说,我想找出有一个节点的数量 child 并且无法弄清楚我的代码有什么问题:

我是这样定义树的

(define (make-tree v left-tree right-tree)
  (list v left-tree right-tree))

(define (value T) (car T))

(define (left T) (cadr T))

(define (right T) (caddr T))

查找节点数的代码:

(define (count-one-child T)
  (let* ((sum 0))
    (cond ((null? T) 0)
          ((and (null? (left T))(null? (right T))) sum)
          ((and (null? (left T)) (not (null? (right T))))
           (begin (set! sum (+ 1 sum)) (count-one-child (right T)) sum))
          ((and (null? (right T))(not (null? (left T))))
           (begin (set! sum (+ 1 sum)) (count-one-child (left T)) sum))
          (else (begin (count-one-child (left T)) (count-one-child (right T)))))))

在 Scheme 中编写过程时应避免使用 set!,这是在其他编程语言中考虑解决方案的必要方式,但在 Scheme 中不是正确的方法。

要解决这个问题,就是要通盘考虑,条件合适才加上1。并且不要忘记推进递归,并组合每个子树的结果。试试这个:

(define (count-one-child T)
        ; empty tree
  (cond ((null? T) 0)
        ; a leaf
        ((and (null? (left  T)) (null? (right T))) 0)
        ; only right subtree exists, add 1
        ((null? (left  T))
         (+ 1 (count-one-child (right T))))
        ; only left subtree exists, add 1
        ((null? (right T))
         (+ 1 (count-one-child (left  T))))
        ; both subtrees exist
        (else
         (+ (count-one-child (left  T))
            (count-one-child (right T))))))

正如 Óscar 在他的回答中提到的那样,使用突变并不是首选方法,但是我看到您已经问过如何使用突变来做到这一点,这就是方法:

(define (count-one-child T)
  (define sum 0)
  (define (aux T)
    (let* ((l (left T))
           (r (right T))
           (nulll? (null? l))
           (nullr? (null? r)))      
      (if nulll?
          (cond ((not nullr?)
                 (set! sum (+ 1 sum))
                 (aux r)))
          (cond (nullr?
                 (set! sum (+ 1 sum))
                 (aux l))
                (else
                 (aux l)
                 (aux r))))))
  (when (not (null? T))     
    (aux T))

  sum)

如您所见,sum 在递归过程 aux 之外,否则每个递归的 sum 变量都是不同的。在上面的代码中,助手可以访问在应用助手之前创建的 mutate sum

不需要太多重写就可以正常运行。您可以将它作为参数,而不是改变变量,并在递归的同时更新它:

(define (count-one-child T)
  (define (aux T sum)
    (let* ((l (left T))
           (r (right T))
           (nulll? (null? l))
           (nullr? (null? r)))
      (if nulll?
          (if nullr?
              sum
              (aux r (+ 1 sum)))
          (if nullr?
              (aux l (+ 1 sum))
              (aux l (aux r sum))))))
  (if (null? T)
      0
      (aux T 0)))

这与变异版本一样好,甚至可能稍微好一点。