#lang racket (provide (contract-out [new-heap (-> heap?)] [insert! (-> heap? real? void?)] [remove-min! (-> heap? real?)] [get-min (-> heap? real?)] [heap-size (-> heap? natural?)])) (struct heap (size vec) #:mutable) ;; heap? : any/c -> boolean? -- predicate ;; heap-size : heap? -> any/c -- selector ;; heap-vec : heap? -> any/c -- selector ;; heap : any/c any/c -> heap? -- constructor ;; -- also comes with lots of info at compile time ;; -- that match (and other stuff) uses ;; set-heap-size! : heap? any/c -> void -- mutator ;; set-heap-vec! : heap? any/c -> void -- mutator ;; The children of index n are 2*n+1 and 2*n+2 when you ;; number in order across the levels of the tree, eg: ;; ;; 0 ;; / \ ;; / \ ;; 1 2 ;; / \ / \ ;; 3 4 5 6 ;; / \ ;; 7 8 ;; parent of index n is at index floor((n-1)/2). ;; check by substituting into the formulas above: ;; floor(((2*n+1)-1)/2) = floor(2*n/2) = n ;; floor(((2*n+2)-1)/2) ;; = floor((2*n+1)/2) ;; = floor(n+1/2) = n (define (insert! h n) (grow-vec-if-needed! h) (match-define (heap hsize vec) h) (define vec-size (vector-length vec)) (vector-set! vec hsize n) ;; vec[size] = n (bubble-up h) (set-heap-size! h (+ hsize 1))) (define (bubble-up h) (match-define (heap hsize vec) h) (let loop ([n hsize]) (define p (parent-index-of n)) (when (p . >= . 0) (define pv (vector-ref vec p)) (define nv (vector-ref vec n)) (unless (<= pv nv) (swap! vec p n) (loop p))))) ;;; grow-vec-if-needed! : heap -> void (define (grow-vec-if-needed! h) (match-define (heap hsize vec) h) (define vsize (vector-length vec)) (when (vsize . <= . hsize) (define new-vec (make-vector (* 2 vsize) 0)) (for ([i (in-range vsize)]) (vector-set! new-vec i (vector-ref vec i))) (set-heap-vec! h new-vec))) ;; h.vec = new-vec (define (remove-min! h) (match-define (heap hsize vec) h) (define min (vector-ref vec 0)) (vector-set! vec 0 (vector-ref vec (- hsize 1))) (bubble-down h) (set-heap-size! h (- hsize 1))) (define (bubble-down h) (match-define (heap hsize vec) h) (let loop ([n 0]) ;; in class we didn't come to a consensus on what the ;; argument of `parent-index-of` should be (and it may ;; be that there is more than one way to think about ;; this that works). ;; we did decide that there are three intersting cases, ;; based on how many children the node we're looking at ;; has and that there are three cases: ;; - two children ;; - left child only ;; - no children ;; there can never be a right-child only ;; the class seemed to be moving towards the idea that ;; we can figure out what the index of the parent of ;; the last node (or the parent of the first empty space) ;; is and then use that to determine which case we're in. (define parent-of-empty-space (parent-index-of (- hsize 2))) (when (n . <= . hsize) (define l (+ (* 2 n) 1)) (define r (+ (* 2 n) 2)) (define nv (vector-ref vec n)) (define lv (vector-ref vec l)) (define rv (vector-ref vec r)) (cond [(and (< nv lv) (< nv rv)) (void)] [(< lv rv) (swap! vec n l) (loop l)] [(< rv lv) (swap! vec n r) (loop r)])))) (define (parent-index-of n) (floor (/ (- n 1) 2))) (define (swap! vec i j) (define old (vector-ref vec i)) (vector-set! vec i (vector-ref vec j)) (vector-set! vec j old)) (define (get-min h) (vector-ref (heap-vec h) 0)) (define (new-heap) (heap 0 (vector "an inital thingy that isn't a number")))