#| A binary-tree is either: - #f - (make-bt val left right) where val is a number, and left and right are binary-trees. |# (define-struct bt (val left right)) ;; example binary trees (define n1 (make-bt 1 #f #f)) (define n2 (make-bt 2 #f #f)) (define n3 (make-bt 3 #f #f)) (define m12 (make-bt 12 n1 n2)) (define r123 (make-bt 123 m12 n3)) #| Template: (define (binary-tree-template a-bt) (cond [(boolean? a-bt) ...] [else ... (bt-val a-bt) ... ... (binary-tree-template (bt-left a-bt)) ... (binary-tree-template (bt-right a-bt))])) |# ;; flatten : binary-tree -> list-of-numbers ;; to construct a list of the numbers in the binary tree (define (flatten a-bt) (cond [(boolean? a-bt) '()] [else (cons (bt-val a-bt) (append (flatten (bt-left a-bt)) (flatten (bt-right a-bt))))])) ;; tests (flatten #f) '() (flatten n1) '(1) (flatten r123) '(123 12 1 2 3) #| a Path is either: - '() - (cons 'l path) - (cons 'r path) |# ;; follow-directions : binary-tree path -> number ;; finds the number that the path points to in the tree (define (follow-directions a-bt a-path) (cond [(and (boolean? a-bt) (null? a-path)) 'bad-path!!] [(and (bt? a-bt) (null? a-path)) (bt-val a-bt)] [(and (boolean? a-bt) (eq? 'l (car a-path))) 'bad-path!!] [(and (bt? a-bt) (eq? 'l (car a-path))) (follow-directions (bt-left a-bt) (cdr a-path))] [(and (boolean? a-bt) (eq? 'r (car a-path))) 'bad-path!!] [(and (bt? a-bt) (eq? 'r (car a-path))) (follow-directions (bt-right a-bt) (cdr a-path))])) ;; examples as tests: (follow-directions r123 '()) 123 (follow-directions r123 '(l)) 12 (follow-directions r123 '(r)) 3 (follow-directions r123 '(l r)) 2 #| A binary-search-tree is either: - #f - (make-bt val left right) where val is a number, and left and right are binary-search-trees. INVARIANT: for each node, n, in a tree, (bt-val n) is bigger than all numbers in (bt-left n) and smaller than all numbers in (bt-right n). |# ;; there are several binary-search-trees that represent ;; that set. Here are three of them: (define ex-bt1 (make-bt 2 #f (make-bt 4 #f (make-bt 6 #f (make-bt 8 #f (make-bt 10 #f (make-bt 12 #f (make-bt 14 #f #f)))))))) (define ex-bt2 (make-bt 14 (make-bt 12 (make-bt 10 (make-bt 8 (make-bt 6 (make-bt 4 (make-bt 2 #f #f) #f) #f) #f) #f) #f) #f)) (define ex-bt3 (make-bt 8 (make-bt 4 (make-bt 2 #f #f) (make-bt 6 #f #f)) (make-bt 12 (make-bt 10 #f #f) (make-bt 14 #f #f)))) ;; union : binary-search-tree binary-search-tree -> binary-search-tree (define (union bt1 bt2) (add-each-to-binary-tree (flatten bt1) bt2)) ;; add-each-to-binary-tree : list-of-numbers binary-tree -> binary-tree (define (add-each-to-binary-tree a-lon bt) (cond [(null? a-lon) bt] [else (add-each-to-binary-tree (cdr a-lon) (add-to-binary-tree (car a-lon) bt))])) ;; add-to-binary-tree : number binary-tree -> binary-tree ;; constructs a new binary tree that contains ;; all of the elements from bt, with n added in. (define (add-to-binary-tree n bt) (cond [(boolean? bt) (make-bt n #f #f)] [else (cond [(< n (bt-val bt)) (make-bt (bt-val bt) (add-to-binary-tree n (bt-left bt)) (bt-right bt))] [(= n (bt-val bt)) bt] [(> n (bt-val bt)) (make-bt (bt-val bt) (bt-left bt) (add-to-binary-tree n (bt-right bt)))])])) ;; examples as tests (add-to-binary-tree 1 #f) (make-bt 1 #f #f) (add-to-binary-tree 1 (make-bt 2 #f #f)) (make-bt 2 (make-bt 1 #f #f) #f) (add-to-binary-tree 3 (make-bt 2 #f #f)) (make-bt 2 #f (make-bt 3 #f #f)) (union #f #f) #f (union (make-bt 1 #f #f) #f) (make-bt 1 #f #f) (union #f (make-bt 1 #f #f)) (make-bt 1 #f #f) (union (make-bt 1 #f #f) (make-bt 2 #f #f)) (make-bt 2 (make-bt 1 #f #f) #f) (union (make-bt 2 #f #f) (make-bt 1 #f #f)) (make-bt 1 #f (make-bt 2 #f #f)) (union (make-bt 2 #f #f) (make-bt 2 #f #f)) (make-bt 2 #f #f) (union (make-bt 3 (make-bt 2 #f #f) #f) (make-bt 3 (make-bt 1 #f #f) #f)) (make-bt 3 (make-bt 1 #f (make-bt 2 #f #f)) #f) (union (make-bt 3 (make-bt 1 #f #f) #f) (make-bt 3 (make-bt 2 #f #f) #f)) (make-bt 3 (make-bt 2 (make-bt 1 #f #f) #f) #f) (union ex-bt1 ex-bt2) ex-bt2 (union ex-bt2 ex-bt1) ex-bt1 (union ex-bt3 ex-bt1) ex-bt1 (union ex-bt1 ex-bt3) ex-bt3 #| Analysis: there are two cases to consider in union. Clearly, in the first case, if the inputs are binary search trees, so is the output. In the second case, since each recursive call produces a binary search tree, we only have to show that the input to each recursive call is also a binary search tree and that add to binary tree produces a binary search tree. Since each subtree is a binary search tree, we know that the inputs to each recursive call is correct. For add-to-binary-tree, consider both cases. Clearly, in the first case, the result is a binary search tree. In the second case, there are 3 subcases. The middle one is obvious: the input must have been a binary search tree, so the output is too. For the first case, we know that n is less than the top number in the tree, and since bt is a binary search tree, it is less than all of the nodes. Thus, we know that if we add n to the left subtree and build a new binary tree, that will be a binary search tree. Same thing for the right hand side. |# ;; inorder : binary-tree -> list-of-numbers ;; produces a list of all of the numbers in the ;; binary tree, ordered from smallest to largest (define (inorder a-bt) (cond [(boolean? a-bt) '()] [else (append (inorder (bt-left a-bt)) (cons (bt-val a-bt) '()) (inorder (bt-right a-bt)))])) (inorder #f) '() (inorder (make-bt 1 #f #f)) '(1) (inorder (make-bt 1 #f (make-bt 2 #f #f))) '(1 2) (inorder (make-bt 2 (make-bt 1 #f #f) #f)) '(1 2) (inorder ex-bt1) '(2 4 6 8 10 12 14) (inorder ex-bt2) '(2 4 6 8 10 12 14) (inorder ex-bt3) '(2 4 6 8 10 12 14) #| analysis Because the left subtree of each binary tree has numbers smaller than the current node, all of those numbers must come first in the list. Similarly, the right subtree has numbers larger than the current node, so those numbers must be at the end of the list, which leaves the current number right in the middle. |#