;; CS101 Lecture Notes, Spring 2013 ;; Lecture 21 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; BINARY TREES: ANOTHER SELF-REFERENTIAL DATA STRUCTURE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Binary trees are a commonly-used structure in computer science ; and they have a form similar to a real tree. However, in ; a binary tree, the root is at the top and the leaves are at the ; bottom. Each node in the tree has at most 2 children, left and ; right. ; (define-struct btnode (value left right)) ;;Contracts: ;; CONSTRUCTOR: ;; (make-btnode value btnode btnode) -> btnode ;; ACCESSORS: ;; (btnode-value btnode) -> number ;; (btnode-left btnode) -> btnode ;; (btnode-right btnode) -> btnode ;; MUTATORS: ;; (set-btnode-value! btnode number) -> void ;; (set-btnode-left! btnode btnode) -> void ;; (set-btnode-right! btnode btnode) -> void ;; TYPE-CHECKER: ;; (btnode? anything) -> boolean ; ; This definition contains fields that refer to objects of the ; same type, but there is no base case. We need a btnode struct ; in order to create another btnode, just like we needed a list ; before we could create a longer list. ; ; We'll use the value empty to indicate a btnode that has no left ; and/or right "children". This definition gives us a base case ; by which we can create the leaves of the tree. ; ;; Data definition of a binary tree (BT) ;; A binary tree (BT) is either ;; 1. empty, or ;; 2. it's a (make-btnode num l r), where num is a number, ;; l is a BT and r is a BT ;; Contract: (fun-for-bt BT) -> ??? ;; Header: (define fun-for-bt (lambda (abt) ... )) ;; Purpose: skeleton for function that consumes a BT ;(define fun-for-bt ; (lambda (abt) ;; (cond ;; ;; base case will be empty? as it was for lists ;; [(empty? abt) ...] ;; ;; recursive case requires 2 recursive calls, one for the ;; ;; left subtree and one for the right ;; [ else (...(btnode-value abt) ;; (fun-for-bt (btnode-left abt)) ;; (fun-for-bt (btnode-right abt)))) ;; Example binary trees (bt's): (define bt0 (make-btnode 24 empty empty)) ;; a leaf (define bt1 (make-btnode 17 empty bt0)) ;; an internal node ;; with one child ;; A BT with 4 internal nodes: (define bt2 (make-btnode 15 (make-btnode 87 empty empty) bt1)) ;; bt2 is a root node with a leaf for a left btnode and a ;; an internal node for the right btnode ; bt2: ; 15 ; / \ ; 87 17 ; / \ / \ ; mt mt mt 24 ; / \ ; mt mt ; mt = empty ; Because we need a tree to create a tree (something like we need empty ; to create a list), we need to build the tree from the bottom (the leaves) ; to the top (the root). In Tree-A, node e-63 is the root and any node ; with empty for the left and right fields is a leaf. ;; Draw pictures of the following binary tree: (define a-10 (make-btnode 10 empty empty)) (define c-24 (make-btnode 24 empty empty)) (define f-77 (make-btnode 77 empty empty)) (define i-99 (make-btnode 99 empty empty)) (define b-15 (make-btnode 15 a-10 c-24)) (define h-95 (make-btnode 95 empty i-99)) (define d-29 (make-btnode 29 b-15 empty)) (define g-89 (make-btnode 89 f-77 h-95)) (define e-63 (make-btnode 63 d-29 g-89)) (define Tree-A e-63) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Develop contains-bt?. The function consumes a number and a ; BT and determines whether the number occurs in the tree. ; Contract: (contains-bt? num BT) -> boolean ; Header: (define contains-bt? (lambda (n abt) ... )) ; Purpose: Checks whether n is a btnode-number in abt ; Pre-function tests: (check-expect (contains-bt? 77 Tree-A) true) (check-expect (contains-bt? 42 Tree-A) false) (check-expect (contains-bt? 3 empty) false) ; (define contains-bt? ; (lambda (n abt) ; (cond ; ;; base case 1: if at the empty list, return false ; [(empty? abt) #f] ; ;; base case 2: if value in current node = n, return true ; [(= (btnode-value abt) n) #t] ; ;; recursive case: keep looking in the left and right subtrees of abt ; [else ; (or (contains-bt? n (btnode-left abt)) ; (contains-bt? n (btnode-right abt ;; ALTERNATE VERSION WITH NO EXPLICIT BOOLEAN LITERALS: (define contains-bt? (lambda (n abt) ;; in order for the value to be found, it must be the case that ;; the tree is not empty AND either the value contained in the ;; current node OR some value contained in the left subtree OR ;; some value contained in the right subtree is equal to n. (and (not (empty? abt)) (or (= (btnode-value abt) n) (contains-bt? n (btnode-left abt)) (contains-bt? n (btnode-right abt)))))) ; ; A "binary search tree" (bst) is a subset of BT's. ; ; A bst is either: ; 1. empty, or ; 2. (make-btnode value left right) where ; a. left and right are bsts, ; b. all numbers in left are smaller than value, ; c. all numbers in right are larger than value, and ; d. all numbers in the tree are unique. ; ; This definition places constraints on the way we construct the ; bst. When adding numbers to a bst, we must inspect the current ; values in the tree to make sure the number is inserted in the ; correct position and that it doesn't occur more than once. ; ;; Every new node that is inserted into a bst is a "leaf" node. ;; The "shape" of a bst is determined by the order in which ;; the nodes are inserted into the tree. ; ; Define the function insertBST that consumes a number and a bst ; and inserts the number at the correct position in the bst ; such that the end result is a bst. Remember that this function ; must return a bst, so the tree must be rebuilt as the proper ; place to insert the value is found. ; ;; Contract: (insertBST number bst) -> bst ;; Header: (define insertBST (lambda (num bst) ... )) ;; Purpose: insert the given number into the given bst ;; Pre-function tests: ;; insert single node into tree (check-expect (insertBST 8 empty) (make-btnode 8 empty empty)) ;; insert duplicate value into tree (check-expect (insertBST 8 (insertBST 8 empty)) (make-btnode 8 empty empty)) ;; insert right child into tree (check-expect (insertBST 10 (insertBST 8 empty)) (make-btnode 8 empty (make-btnode 10 empty empty))) ;; insert left child into tree (check-expect (insertBST 5 (insertBST 10 (insertBST 8 empty))) (make-btnode 8 (make-btnode 5 empty empty) (make-btnode 10 empty empty))) ;; Function definition: (define insertBST (lambda (num bst) ;; if empty?, return num in a btnode with no children (cond [(empty? bst) (make-btnode num empty empty)] ;; if num is less than the current btnode-value, call the ;; insertBST function recursively on btnode-left [(< num (btnode-value bst)) (make-btnode (btnode-value bst) (insertBST num (btnode-left bst)) (btnode-right bst))] ;; if num is greater than the current bt-node-number, call the ;; insertBST function recursively on current bt-node-right [(> num (btnode-value bst)) (make-btnode (btnode-value bst) (btnode-left bst) (insertBST num (btnode-right bst)))] ;; if num is equal to the current bt-node-number, do nothing [else bst])))