Newsgroups: comp.lang.scheme
Path: cantaloupe.srv.cs.cmu.edu!rochester!udel!news.sprintlink.net!howland.reston.ans.net!swrinde!sgigate.sgi.com!sdd.hp.com!hplabs!hplntx!hplntx.hpl.hp.com!gjr
From: gjr@hplgr2.hpl.hp.com (Guillermo (Bill) J. Rozas)
Subject: Re: AVL Trees
Sender: news@hpl.hp.com (HPLabs Usenet Login)
Message-ID: <GJR.95Oct3133505@hplgr2.hpl.hp.com>
In-Reply-To: rosie@cs.oberlin.edu's message of Fri, 29 Sep 1995 17:32:20 -0400
Date: Tue, 3 Oct 1995 21:35:05 GMT
Reply-To: gjr@hplabs.hpl.hp.com
References: <rosie-2909951732200001@rm113a.bailey.oberlin.edu>
Nntp-Posting-Host: hplgr2.hpl.hp.com
Organization: /users/gjr/.organization
Lines: 299

In article <rosie-2909951732200001@rm113a.bailey.oberlin.edu> rosie@cs.oberlin.edu (Rosemary de Fremery) writes:

|   From: rosie@cs.oberlin.edu (Rosemary de Fremery)
|   Newsgroups: comp.lang.scheme
|   Subject: AVL Trees
|   Date: Fri, 29 Sep 1995 17:32:20 -0400
|   Organization: Electric Ladyland [Oberlin College]
|   Lines: 11
|
|   Hello all...  I'm currently searching the web for information
|   on AVL trees and have come up empty, to my frustration.
|   I've already checked the Scheme home page and Scheme
|   Underground sites, as well as Indiana University's Scheme
|   Repository.  Any suggestions?  Thanks in advance!
|
|   --Rosemary de Fremery

The MIT Scheme "microcode" uses AVL trees internally for some work.
The code is written in C, but it was written by transliteration of the
attached Scheme program (I always debug my non-trivial algorithms in
Scheme first).

I'm afraid that it is specific to MIT-Scheme, but you may be able to
port it to some other dialect.

Most of the code is debugging code to create and print such trees.
Only insert is implemented because that is what I needed.


;;; -*- Scheme -*-

(declare (usual-integrations))

;;; AVL trees.
;;; The difference in height of the branches of a node is at most one.
;;; Lookup and insert (and delete) are all o(h) operations,
;;; and log(n,2) <= h <= log(n,phi), where phi is the "golden ratio".

(define-structure (node
		   (conc-name node/)
		   (constructor node/make))
  (entry false read-only true)
  (height false read-only false)
  (left false read-only false)
  (right false read-only false))

(define-integrable (leaf/make entry)
  (node/make entry 1 false false))

(define-integrable (branch/height branch)
  (if (not branch)
      0
      (node/height branch)))

(define (vector->tree v)
  (sorted-vector->tree (sort v string<?)))

(define (sorted-vector->tree v)
  (sorted-subvector->tree v 0 (vector-length v)))

(define (sorted-subvector->tree v lo hi)
  (cond ((< (1+ lo) hi)
	 (let ((middle (quotient (+ lo hi) 2)))
	   (let ((left (sorted-subvector->tree v lo middle))
		 (right (sorted-subvector->tree v (1+ middle) hi)))
	     (node/make (vector-ref v middle)
			(1+ (max (branch/height left)
				 (branch/height right)))
			left
			right))))
	((>= lo hi)
	 false)
	(else
	 (leaf/make (vector-ref v lo)))))

(define (tree->sorted-vector tree)
  (list->vector
   (let walk ((node tree)
	      (accum '()))
     (if (not node)
	 accum
	 (walk (node/left node)
	       (cons (node/entry node)
		     (walk (node/right node)
			   accum)))))))

(define (tree->list tree)
  (and tree
       (list (list (node/entry tree)
		   (node/height tree))
	     (tree->list (node/left tree))
	     (tree->list (node/right tree)))))

(define (copy-tree tree)
  (let copy ((node tree))
    (and node
	 (node/make (node/entry node)
		    (node/height node)
		    (copy (node/left node))
		    (copy (node/right node))))))

(define (verify-tree tree)
  (let ((result true))
    (define (lose message node)
      (set! result false)
      (warn message node))

    (define (verify-balance node)
      (if node
	  (begin
	    (if (not (<= -1
			 (- (branch/height (node/left node))
			    (branch/height (node/right node)))
			 1))
		(lose "verify-tree: Unbalanced" node))
	    (verify-balance (node/left node))
	    (verify-balance (node/right node)))))

    (define (verify-heights node)
      (if node
	  (begin
	    (if (not (= (node/height node)
			(1+ (max (branch/height (node/left node))
				 (branch/height (node/right node))))))
		(lose "verify-tree: Heights wrong" node))
	    (verify-heights (node/left node))
	    (verify-heights (node/right node)))))

    (define (verify-sorted tree)
      (let* ((vu (tree->sorted-vector tree))
	     (vs (sort vu string<?)))
	(if (not (equal? vu vs))
	    (lose "verify-tree: Not sorted" tree))))

    (verify-heights tree)
    (verify-sorted tree)
    (verify-balance tree)
    result))

(define (tree/insert! tree entry)
  (define (update-height! node)
    (set-node/height!
     node
     (1+ (max (branch/height (node/left node))
	      (branch/height (node/right node))))))

  (define (rotate->left! node)
    (let* ((right (node/right node))
	   (beta (node/left right)))
      (set-node/right! node beta)
      (set-node/left! right node)
      (update-height! node)
      (update-height! right)
      right))

  (define (rotate->right! node)
    (let* ((left (node/left node))
	   (beta (node/right left)))
      (set-node/left! node beta)
      (set-node/right! left node)
      (update-height! node)
      (update-height! left)
      left))

  (define (rebalance-left! node)
    ;; Just added a node to the left
    (if (>= (1+ (branch/height (node/right node)))
	    (branch/height (node/left node)))
	(begin
	  (update-height! node)
	  node)
	(let ((q (node/left node)))
	  (if (<= (branch/height (node/right q))
		  (branch/height (node/left q)))
	      (rotate->right! node)
	      (begin
		(set-node/left! node (rotate->left! q))
		(rotate->right! node))))))

  (define (rebalance-right! node)
    ;; Just added a node to the right
    (if (>= (1+ (branch/height (node/left node)))
	    (branch/height (node/right node)))
	(begin
	  (update-height! node)
	  node)
	(let ((q (node/right node)))
	  (if (<= (branch/height (node/left q))
		  (branch/height (node/right q)))
	      (rotate->left! node)
	      (begin
		(set-node/right! node (rotate->right! q))
		(rotate->left! node))))))

  (define (walk node)
    (cond ((not node)
	   (leaf/make entry))
	  ((string<? entry (node/entry node))
	   ;; Go to the left
	   (set-node/left! node (walk (node/left node)))
	   (rebalance-left! node))
	  ((string<? (node/entry node) entry)
	   ;; Go to the right
	   (set-node/right! node (walk (node/right node)))
	   (rebalance-right! node))
	  (else
	   (warn "tree/insert!: Duplicate entry" entry node)
	   node)))

  (walk tree))

(define (generate-random-string len)
  (let ((string (make-string len)))
    (do ((i 0 (1+ i)))
	((= i len) string)
      (vector-8b-set! string i (+ 48 (random 10))))))

(define (test-tree-code count slen)
  (let loop ((tree false)
	     (count count))
    (if (zero? count)
	tree
	(let ((copy (copy-tree tree))
	      (entry (generate-random-string slen)))
	  (let ((new (tree/insert! copy entry)))
	    (if (not (verify-tree new))
		(error "test-tree-code: Lose" tree entry new))
	    (loop new (-1+ count)))))))

(define (new-test count slen)
  (let ((result
	 (let loop ((tree false)
		    (count count))
	   (if (zero? count)
	       tree
	       (loop (tree/insert! tree (generate-random-string slen))
		     (-1+ count))))))
    (if (not (verify-tree result))
	(error "new-test: Lose" result))
    result))

(define (print-tree tree)
  (define (print-entry prefix state entry)
    (write-string prefix)
    (write-string (case state
		    ((LEFT) " /")
		    ((RIGHT) " \\")
		    (else "")))
    (write-string entry)
    (newline))

  (define (print-bar prefix state branch)
    (write-string prefix)
    (write-string (cond ((eq? state branch)
			 "  ")
			((eq? state 'START)
			 "")
			(else
			 " |")))
    (write-string " |")
    (newline))

  (define (print-branch state node prefix branch)
    (walk node
	  (cond ((eq? state branch)
		 (string-append prefix "  "))
		((eq? state 'START)
		 prefix)
		(else
		 (string-append prefix " |")))
	  branch))

  (define (walk node prefix state)
    (if (not node)
	(print-entry prefix state "- nil")
	(begin
	  (if (node/left node)
	      (begin
		(print-branch state
			      (node/left node)
			      prefix
			      'LEFT)
		(print-bar prefix state 'LEFT)))
	  (print-entry prefix state
		       (string-append "-+- "
				      (node/entry node)))
	  (if (node/right node)
	      (begin
		(print-bar prefix state 'RIGHT)
		(print-branch state
			      (node/right node)
			      prefix
			      'RIGHT))))))

  (begin
    (newline)
    (newline)
    (walk tree "" 'START)
    (newline)))
