二叉树中的路径

时间:2019-01-17 16:43:07

标签: list scheme b-tree

我正在为考试做准备,我几乎做了所有事情,但是我不理解该练习。如何使函数能够从特定值在二叉树中搜索路径。

这里是一个示例:

(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
(find-path tree 4)
(1 2 4)

1 个答案:

答案 0 :(得分:1)

我开始草绘一些代码-

(define (find t q) ;; find q in t

(path empty) ;; the return path will be a list, we'll start it off as an empty list

(if (empty? t) ;; fundamental laws: always check for empty list first
    #f         ;; if the tree is empty, there is nothing to find, we use #f to signal this
    (if (eq? q (car t)) ;; otherwise we can check if the node matches q ...
        ;; wups we can't do eq? test yet, it's possible `(car t)` is a list of nodes
        ))

我怎么看?我看一下我们的输入列表-

(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))
  1. 我们总是先检查empty?
  2. 如果列表不为空,则我们知道:
    • 至少一个元素(car tree)
    • 其余元素(cdr tree)
  3. 我将最外面的列表的元素可视化;只有三个:
    • 1
    • (2 (3) (4))
    • (5 (6 (7) (8)) (9))
  4. 第一个元素是1,所以我认为我可以拿到eq?并立即检查它是否匹配q
  5. 我注意到第二个元素是不同的 type 。直观上,我们无法将单个元素与元素列表进行匹配,因此我们必须在尝试list?
  6. 之前在{em> 之前处理eq?情况

修复我的嘘声-

(define (find t q)

(path empty)

(if (empty? t)
    #f
    (if (list? (car t))
        ;; when the node is a list of nodes
        (if (eq? q (car t))
            ;; when the node matches q
            ;; when the node does not match q
            )))

将折叠if链接到cond以获得更好的可读性-

(define (find t q)

(path empty)

(cond ((empty? t)
        #f)
      ((list? (car t))
       ;; when the node is a list of nodes
       )
      ((eq? q (car t))
       ;; when the node matches q
       )
      (else
       ;; when the node does not match q
       ))

该代码更加扁平,现在更易于阅读。其中一些空白很难填写,但我被第二个空白吸引了。当q等于(car t)时,表示我们找到了一个匹配项,现在该返回path-

(define (find t q)

(path empty)

(cond ((empty? t)
        #f)
      ((list? (car t))
       ;; when the node is a list of nodes
       ;; we'll come back to this ...
       )
      ((eq? q (car t))
       (cons q path)) ;; return the path with the final node
      (else
       ;; when the nodes does not match q
       ;; and save this for later too ...
       ))

好的,还不错。因此,我检查了(car t)q匹配的时间,现在我不得不说当不匹配时会发生什么。当(car t)不匹配时,我将其添加到path并以某种方式检查q是否与节点的任何子节点(cdr t)-

(define (find t q)

(path empty)

(cond ((empty? t)
        #f)
      ((list? (car t))
       ;; when node is a list of nodes
       ;; we'll come back to this ...
       )
      ((eq? q (car t))
       (cons q path))
      (else

       ;; add the node to the path ...
       (cons (car t) path)

       ;; check the node's children for a match
       (find (cdr t) q)

       ;; this doesn't quite work ...
       ))

我遇到一种情况,我们需要用新节点更新path,并且需要调用没有find参数的path。为了解决这个问题,我引入了一个循环,该循环允许我们使用指定的任何参数来重复计算表达式-

(define (find t q)

(let loop ;; lazily and sloppily insert a named loop

((path empty) ;; initialize the parameters that will change
 (t t))

(cond ((empty? t) ;; the expression to repeat, (cond ...)
        #f)
      ((list? (car t))
       ;; when the node is a list of nodes
       )
      ((eq? q (car t))
       (cons q path))
      (else
       (loop (cons (car t) path) ;; updated path
             (cdr t))))          ;; updated tree

else子句教我如何与节点的子代(节点的列表)进行匹配。毫无疑问,这将使处理代码中的最后一个空白变得更加容易,当节点是节点列表时,该怎么办! -

(define (find t q)

(let loop

((path empty)
 (t t))

(cond ((empty? t)
        #f)
      ((list? (car t))

       ;; we could just recur the loop with 
       (loop path
             (car t))

       ;; but what about (cdr t) in this case?
       (loop path
             (cdr t))

      ((eq? q (car t))
       (cons q path))
      (else
       (loop (cons (car t) path)
             (cdr t))))

这里的最后一个问题是我要检查两(2)个列表; (car t)被确定为列表,而(cdr t)被确定为列表。我必须检查他们两个。一种简单的解决方案是将两个loop调用与or组合在一起。如果一个loop返回#f,则将检查另一个-

(define (find t q)

(let loop

((path empty)
 (t t))

(cond ((empty? t)
        #f)
      ((list? (car t))
       (or (loop path   ;; or represents dysjunction!
             (car t))
           (loop path
             (cdr t))))
      ((eq? q (car t))
       (cons q path))
      (else
       (loop (cons (car t) path)
             (cdr t))))

修正括号,运行自动压头-

(define (find t q)
  (let loop
    ((path empty)
     (t t))
    (cond ((empty? t)
           #f)
          ((list? (car t))
           (or (loop path
                     (car t))
               (loop path
                     (cdr t))))
          ((eq? q (car t))
           (cons q path))
          (else
           (loop (cons (car t) path)
                 (cdr t))))))

(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))

(find tree 4)
;; '(4 2 1)

(find tree 8)
;; (8 6 5 1)

(find tree 9)
;; (9 5 1)

观察到结果是相反的,因为实际上path是按相反的顺序构造的。返回路径的退出条件只需要在返回-

之前调用reverse
(define (find t q)
  (let loop
    ((path empty)
     (t t))
    (cond ((empty? t)
           #f)
          ((list? (car t))
           (or (loop path
                     (car t))
               (loop path
                     (cdr t))))
          ((eq? q (car t))
           (reverse (cons q path))) ;; don't forget to reverse!
          (else
           (loop (cons (car t) path)
                 (cdr t))))))

(define tree '(1 (2 (3) (4)) (5 (6 (7) (8)) (9))))

(find tree 4)
;; '(1 2 4)

(find tree 8)
;; (1 5 6 8)

(find tree 9)
;; (1 5 9)