-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathch11.tex
More file actions
executable file
·1765 lines (1628 loc) · 73.8 KB
/
Copy pathch11.tex
File metadata and controls
executable file
·1765 lines (1628 loc) · 73.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\chapter{Search Trees}
\label{ch:search-trees}
\section{Finding Things}
Consider the problem of keeping things organized so you can find them.
It's not hard if you don't have many things.
If you have a dozen pairs of socks, you can just throw them in a drawer
and find the pair you want quickly, without really having to search.
However, if you run an office supply store that carries thousands of
types of paper, envelopes, pencils, and erasers,
you'll need to arrange them carefully so people can find them.
The importance of being organized increases with the number of items.
If you run a warehouse, you might have to keep track of many thousands of things.
One solution is to associate a stock number with each
kind of item, set up a bin for each, and arrange the bins by
\index{stock number search}stock number.
That works reasonably well, even if there are many thousands of kinds of items.
When the warehouse decides to stock a new kind of item, you just set up
a bin at the end of the line and give it a stock number higher than the
one before it.
However, what if an item gets discontinued?
You can empty out its bin, but what do you do with the space?
It might be okay to leave the space empty for the first few discontinuations,
but with many thousands of items, there will be, over time, thousands
of discontinuations, and you'll end up wasting a lot of space.
Furthermore, stock numbers solve the problem of finding the
right bin, but they don't solve the problem of remembering the stock number
that goes with, say, 20-pound white paper versus color ink cartridges.
To be able to find the stock number associated with
an item, you'll need some kind of index that arranges the items in
categories, subcategories, and the like, and perhaps in alphabetical
order by name within each category.
When the warehouse discontinues an item,
you need to cross it out in the index,
and when a new kind of item comes in, you need to write it into
the index at the appropriate place.
Eventually, the index is a mess and needs reprinting.
You might be able to make this work, even for many thousands of items,
but you can see that the idea doesn't scale up well, and when you're
trying to keep track of many millions of things, as you might need to
do, for example, if you were the data manager for a large company,
keeping lists in order by name or stock number becomes
untenable. New kinds of data need to be added every day, or even every
few seconds, and obsolete items need to be deleted.
So, keeping the items in a list starting with the first item,
then the second, third, and so on cannot work in practice.
It will require too much data movement to expand spaces between
items to insert new ones or collapse space between items to delete old ones.
The problem needs a better solution,
and that's what this chapter is about.
Let's recap the problem.
\emph{Pile-of-socks method}.
If you have only a few items to keep track of,
the \index{socks, pile of}\index{pile of socks}pile-of-socks method works.
When you get a new pair of socks, you just throw it in the drawer.
When an old pair wears out, you just throw it away.
If you want to locate a particular pair of socks,
you just look through the drawer,
pair by pair, until you find the one you're looking for.
If you have ten pairs, the first pair you look at might be
the one you want, or the second, or third.
At worst, you'll have to look through all ten pairs.
On average you'll probably find the right pair
after looking through about half of them.
\emph{Binary search method}.\label{binary-search-method}
If you had a thousand books on your bookshelves,
you could arrange them in alphabetical order by title.
When you get a new book, you just insert it in the right place,
maybe sliding a few books around to accommodate it.
If you lose one, you can just leave the slot empty,
or slide the books around to fill in the space.
To find a particular book, you would probably just guess about
where it would be, look there, and adjust your next guess accordingly.
However, there is an efficient way to manage your search that
guarantees finding it in just a few steps. It's called \emph{binary search}.
First, you look at the middle book.
If it's the one you want, great. You're done.
If not, the title you're looking for will either
come after the middle one or before it in alphabetical order.
In either case, the number of possibilities has been cut in half,
and you can repeat the procedure to find the book you're looking for
among the remaining possibilities.
Since you cut the number of books in half each time,
it will not take more than ten steps to locate
a particular book among a thousand books
because cutting a thousand in half, then cutting five hundred in half,
and so on, you get down to one book in ten steps.
Looking at it from the other direction, starting from one
and doubling at each stage, it goes 1, 2, 4, 8, 16, 32, 64,
128, 256, 512, 1024. These are the powers of two:
$2^0$, $2^1$, $2^2$, $2^3$ \dots $2^{10}$.
So, the number of items you need to examine
in the binary search procedure
when there are $n$ items altogether is the
first integer $k$ that makes $2^k \geq n$, or
what amounts to the same thing, $k \geq log_2(n)$.
That is, the number of steps in a
\index{search!binary}\index{binary search}binary search through
$n$ items is \index{logarithm}$log_2(n)$ rounded up
to the next integer.
When you round a number up to the next integer, you get the
\index{ceiling brackets}\index{brackets!ceiling}\emph{ceiling}
of the number:
$\lceil x \rceil$ $=$ ceiling of $x$ $=$ $x$ rounded up to the next integer.
With this notation, the formula for the maximum number of steps
required to locate a particular item using
binary search with a collection of $n$ items
is $\lceil log_2(n) \rceil$.
This gives binary search an amazing
advantage in speed over the pile-of-socks method.
It reduces the number of steps required to find a particular item
in a collection of $n$ items from an average of $n/2$
in the pile-of-socks approach
to a maximum of $\lceil log_2(n) \rceil$
using binary search.
That's $(n/2)/\lceil log_2(n) \rceil$ times faster,
and $(n/2)/\lceil log_2(n) \rceil$ turns
out to be a very big number when there are a lot of items.
\index{search!binary vs linear}For 1,000 items,
binary search is, on average,
$(1{,}000/2)/\lceil log_2(1{,}000) \rceil$ times faster than the pile-of-socks method.
That's about 50 times faster.
For 10,000 items, it's about 350 times faster.
The speed-up factor rises to over 3,000 for 100,000 items,
and for 1,000,000 items, binary search is, on average,
over 25,000 times faster than the pile-of-socks method.
It gets better and better the more items you have,
and in the computer search business, it is common to search for one item among
millions, billions, or even trillions of items.\footnote{For the record,
binary search is 17 million times faster for a billion items
and 12 billion times faster for a trillion items.
The pile-of-socks method would not be a feasible way
to find one item among billions if you had to make such a search
millions of times every day.}
So, binary search, or something as good or better, is indispensable.
The good news is that we can rely on it.
The bad news is that, while it works
well for a few thousand items, the problem of
keeping the items arranged in order
becomes unwieldy, to say the least, when there are
millions of items.
That's what happens when the items are entries in
an \index{search!array}\index{array!search}array of $n$ elements,
numbered, say, 0 to $n - 1$.
Whenever you add a new item or delete an old one,
you have to move things around, one way or the other,
to make space or fill in gaps to
keep the numbers that select array elements
in a contiguous sequence, 0, 1, 2, 3, \dots.
So, keeping items in order with indexes in sequence
to facilitate using binary search
works fine when the items don't change
but becomes infeasible when they do.
In practice, items tend to come and go,
so we need to organize the items in some way
other than just numbering them sequentially.
\section{The AVL Solution}
Not long after computers began to have enough
memory to keep track of many thousands of items,
the mathematicians \index{Adelson-Velskii, Georgy}Adelson-Velskii
and \index{Landis, Evgenii}Landis
devised a structure that
eliminated the problem of sliding things around to
accommodate new items (the \emph{insertion problem})
or get rid of old ones (the \emph{deletion problem}).
This structure, known as the
\index{AVL tree}\index{binary tree!AVL}\index{tree!AVL}AVL tree
(``AV'' for Adelson-Velskii and ``L'' for Landis)\label{AVL-tree},
makes it possible to find an item or
insert a new one
in only about $log(n)$ steps, where $n$ is the number
of items stored in the tree.
It takes about the same number of steps to delete an item,
so the AVL solution provides a practical way to do all three
operations: search, insertion, and deletion.
Adelson-Velskii and Landis solved a very difficult problem
when they figured out how to do this,
but their solution isn't hard to explain
and that is the goal of this chapter.
We will need some terminology to discuss the idea.
The term \label{empty-tree-def}\index{tree!definition}\emph{tree}
will mean either an \emph{empty tree}
or a \label{node-def}\emph{node} called the
\label{root-def}\emph{root}.
A node consists of a sequence of one or more
trees known as
\label{subtree-def}\index{subtree, definition}\emph{subtrees}.\footnote{Yet
another usefully circular (inductive) definition.}
We will focus on \index{binary tree}\index{tree!binary}\emph{binary trees}
in which each node has four components:
%\begin{quote}
\begin{enumerate}
\item a \emph{key},
\item some data associated with the key,
\item a search tree known as the \emph{left subtree}, and
\item a search tree known as the \emph{right subtree}.
\end{enumerate}
%\end{quote}
\label{search-tree-def}\index{search tree}\index{tree!search}\emph{Search trees} are special trees that facilitate finding keys.
The \index{empty tree}\index{tree!empty}\index{search tree!empty}empty tree
is a search tree, by default.
\label{sibling-def}
The two subtrees in a node are called
\index{tree!sibling}\index{sibling, in tree}\index{search tree!sibling}\emph{siblings}.
A key is said to
\label{occurs-in-def}\index{occurs in search tree}\index{search tree!key occurs in}\emph{occur in}
a search tree if it is the key at the root
or if the key occurs in either subtree of the root.
No keys occur in an empty tree.
Keys in search trees must come from a domain that has a
\label{total-ordering-def}\index{total ordering}\index{order!total}\emph{total ordering}.
That is, for any two different keys,
there must be some way to determine
which one precedes the other in the ordering.
For example, when the keys are words made up of lowercase letters,
alphabetic ordering is a total order.
If the keys are numbers, then ordinary numeric ordering is a total order.
As a consequence of having a total order on the keys,
it is possible, given any three keys,
no two of which are the same,
to determine which one comes first,
which comes second, and which comes third.
More generally, any collection of distinct keys
can be arranged in a sequence in increasing order:
each key in the sequence
precedes, in the ordering, the next key in the sequence.
To support binary search,
each node in a search tree
has the property that all of the keys in the left subtree
precede (in the ordering on the domain of keys)
the key of the node, and the key of the node precedes all of
the keys in its right subtree.
A tree with this property is called an
\label{ordered-def}\index{tree!ordered}\index{search tree!ordered}\emph{ordered tree},
so search trees are ordered trees.
\label{binary-search-def}
\index{binary search}\index{tree!searching for key}
To find a key in a search tree,
look first at the key at the root (unless the tree is empty,
in which case
you can conclude that the key does not occur in the tree).
If the key you are looking for is the same as the key at the root,
then you have found the key you're looking for.
If it isn't the same as the key at the root,
it must either precede or follow the root key in the ordering.
If the key you are looking for precedes the root key,
look for it in the left subtree.
If it follows the root key, look for it in the right subtree.
To build a search tree that has a single key,
just construct a node consisting of the key,
its associated data,
an empty left subtree,
and an empty right subtree.
That amounts to inserting the key into an empty search tree.
To insert a key in a nonempty search tree, put it in the left subtree
if the key precedes the root key in the ordering and
in the right subtree if it follows the root key.
\label{insertion-def}
To build a \index{search tree!building}search tree containing
all the keys from a list, simply insert the first one into an empty
search tree, then insert each successive one into the tree
produced by inserting the previous key from the list.
That's the simple part.
The hard part is in making sure the tree
doesn't get too tall.
Nodes in a search tree have subtrees,
and the subtrees have subtrees, and so on.
Eventually, in a binary search,
the desired key is encountered if it occurs in the tree.
If the key doesn't occur in the tree, the search will come to
an empty subtree.
At that point, the search stops with the conclusion that the key
does not occur in the tree.
The
\label{height-is-max-steps}\index{height!tree}\index{tree!height}\index{AVL tree!height definition}\index{search tree!height}\emph{height}
of a search tree is the maximum number of keys
that binary search might need
to examine to find a key in the tree.
Later, we will define the height of a tree formally,
but for now, just think of it as the maximum number of steps
in a binary search for a key in the tree.
AVL trees maintain, in every node,
a balance between the heights of the subtrees in the node.
The AVL method of inserting new nodes or deleting old ones
maintains balance,
which keeps the tree from getting too tall
on one side or the other.
This preserves a high ratio between the number of keys
in the tree and the height of the tree.
That is, the insertion process makes sure
to keep the height of the tree small
compared to the number of keys.
The AVL deletion method does this too.
When a key is inserted or deleted,
the AVL method keeps the height of the tree from exceeding
the base-2 logarithm of the number of keys by more than
\label{50pct-thm}
50 percent.\footnote{The proof
of this fact would take us too far afield, so we are
leaving the proof out of this discussion.}
Therefore, the number of steps in a binary search
of an AVL tree containing $n$ keys
will not exceed $\frac{3}{2}\cdot log_2(n)$ steps.
To have an inkling of how ingenious the AVL solution is,
try to figure out how to insert keys into a search tree
in a way that has the following properties:
\index{balance!AVL tree}\index{AVL tree!balance}\index{search tree!balance}\index{order!AVL tree}\index{AVL tree!order}\index{search tree!order}
%\begin{quote}
\begin{enumerate}
\item The key at each node follows every key that occurs in its left subtree.
\item The key at each node precedes every key in the right subtree.
\item Sibling subtrees differ in height by zero or one.
\end{enumerate}
%\end{quote}
The first two requirements make the search tree ordered,
so that binary search will work.
The last requirement maintains
\label{balance-def}
\emph{balance},
which keeps the tree from getting too tall.
To match the effectiveness of AVL trees,
you would need to define an insertion method
such that neither the height of the tree nor
the number of steps in the insertion
process exceeds the logarithm of the
number of nodes in the tree by more
than a fixed percentage (that is, a percentage that
doesn't depend on the size of the tree).
The number of steps in your method for insertion
might be, for example, up to twice the height of the tree
but not more.
After struggling with the problem of
finding a reliable way to build search trees
that don't get too tall,
you may begin to appreciate the contribution
of Adelson-Velskii and Landis to the problem
of storing large amounts of data so that
individual items can be found quickly
and so that new items can be quickly inserted or
old ones quickly deleted.
\section{Representing Search Trees}
Any detailed discussion of the AVL solution requires
a way to represent search trees.
In our representation,
\label{key-representation}\index{AVL tree!representation}\index{representation!AVL tree}\index{key!AVL tree}keys will be natural numbers
ordered in the usual way.
That is, one key, $k_1$, precedes another key, $k_2$,
if $k_1 < k_2$ in the usual numeric ordering.
That means that the key at a node of a search tree
is numerically greater than all the keys that occur in
the left subtree of the node
and numerically less than
all the keys that occur in the right subtree of the node.
\label{empty-tree-representation}The empty list (\textsf{nil}) will represent the
\index{empty tree}\index{search tree!empty}\index{AVL tree!empty}empty tree.
\label{node-representation}A \index{search tree!node}\index{AVL tree!node}\index{node, in AVL tree}node
in a search tree will be a list of four elements:
\index{representation!AVL tree}\index{AVL tree!representation}\index{search tree!representation}\index{tree!representation}key (a number),
data (of any kind), left subtree, and right subtree.
Those are the essentials of the representation.
\label{root-node-def}
The
\index{root node, AVL tree}\index{search tree!root}\index{tree!root}\index{AVL tree!root}\emph{root node}
of a search tree represents the entire tree.
The formula for the root node, like the formula for any node,
has a key, a left subtree, and a right subtree.
However, unlike other nodes, the root node is not a subtree
of any other node, and all keys in the tree occur
either at the root or in one of its subtrees.
The key at the root is the first element of the list that represents the tree,
followed by the data associated with the root, which is the second
%The key at the root is the first element of the list that represents the tree,
%and the data associated with the root is the second element.
element of the list. \index{AVL tree!representation}\index{search tree!representation}\index{representation!AVL tree}\index{tree!representation} \index{search tree!subtree}\index{AVL tree!subtree}\index{subtree!AVL tree}\label{left-subtree-def}The
third element is the
\index{left subtree (AVL)}\index{right subtree (AVL)}left subtree,
and the fourth element of the list is the \label{right-subtree-def}right subtree.
Figure \ref{fig:searchtree-diagram} shows a formula
for a search tree in which the data are strings.
The figure also displays a diagram of the tree
that the formula represents.
This pictorial way of looking at search trees
will clarify discussions of the insertion process.
\index{tree!diagram}\index{search tree!diagram}\index{AVL tree!diagram}\index{diagram!AVL tree}In a tree diagram, the root node is the one at the top,
and the subtrees dangle from lines going down to
the left and right.
To follow the discussion,
you will need to be able to diagram a tree
given its formula and vice versa.
\begin{figure}
\begin{center}
\includegraphics[scale=1]{images-cmyk/searchtree}
\end{center}
\index{tree!diagram}\index{search tree!diagram}\index{AVL tree!diagram}
\index{diagram!AVL tree}
\caption{Search tree diagram and corresponding formula.}
\label{fig:searchtree-diagram}
\end{figure}
\label{height-def}
\index{height!tree}\index{tree!height}
\index{AVL tree!height operator}\index{search tree!height}
The \emph{height} of an empty tree is zero.
The height of a nonempty tree is one more than that of the
taller of its left and right subtrees.
The size of a search tree is the number of keys that occur in the tree.
No keys occur in an empty tree, so its size is zero.
The size of a nonempty tree is one more than the sum
of the sizes of its left and right subtrees.
We use ACL2 to formalize these definitions
(figure \ref{fig:tree-functions}), %, page \pageref{fig:tree-functions}),
starting with an operator \textsf{mktr} to build a tree from its four components.
Then we define operators to extract keys, data, and subtrees from nodes
(\textsf{key}, \textsf{dat}, \textsf{lft}, and \textsf{rgt}).
Finally, we define predicates to recognize keys and search trees
(\textsf{iskeyp}, \textsf{treep}, \textsf{emptyp})
and to find out whether a key occurs in a tree (\textsf{keyp}).
\begin{figure}
\begin{center}
\begin{code}
\begin{verbatim}
(defun mktr (k d lf rt) ; make tree from
(list k d lf rt)) ; key, data, subtrees
(defun key (s) (first s)) ; key at root
(defun dat (s) (second s)) ; data at root
(defun lft (s) (third s)) ; left subtree
(defun rgt (s) (fourth s)) ; right subtree
(defun emptyp (s) (not (consp s))) ; empty tree?
(defun height (s) ; tree height
(if (emptyp s)
0 ; {ht0}
(+ 1 (max (height (lft s)) (height (rgt s)))))) ; {ht1}
(defun size (s) ; number of keys
(if (emptyp s)
0 ; {sz0}
(+ 1 (size (lft s)) (size (rgt s))))) ; {sz1}
(defun iskeyp (k)
(natp k))
(defun treep (s) ; search tree?
(or (emptyp s)
(and (= (len s) 4) (natp (key s))
(treep (lft s)) (treep (rgt s)))))
(defun keyp (k s) ; key k occurs in s?
(and (iskeyp k) (treep s) (not (emptyp s))
(or (= k (key s)) (keyp k (lft s)) (keyp k (rgt s)))))
\end{verbatim}
\end{code}
\end{center}
\index{equation, by name!\{ht0\}, \{ht1\}}
\index{equation, by name!\{sz0\}, \{sz1\}}
\index{height!tree}\index{tree!height}\index{search tree!height}
\index{operator, by name!height (AVL tree)}\seeonlyindex{height (AVL)}{operator}
\index{AVL tree!height operator}
\index{AVL tree!subtree, extract}
\index{AVL tree!representation, formal}\index{search tree!representation}
\index{tree!representation}\index{representation!AVL tree}
\index{operator, by name!mktr (AVL, make tree)}\seeonlyindex{mktr (AVL)}{operator}
\index{AVL tree!mktr operator}
\index{operator, by name!key (AVL, root key)}\seeonlyindex{key (AVL)}{operator}
\index{AVL tree!key operator}
\index{operator, by name!dat (AVL, root data)}\seeonlyindex{dat (AVL)}{operator}
\index{AVL tree!dat operator}
\index{operator, by name!lft (AVL, left subtree)}\seeonlyindex{lft (AVL)}{operator}
\index{AVL tree!lft operator}
\index{operator, by name!size (AVL tree)}\seeonlyindex{size (AVL)}{operator}
\index{AVL tree!size operator}
\index{operator, by name!rgt (AVL, right subtree)}\seeonlyindex{rgt (AVL)}{operator}
\index{AVL tree!rgt operator}
\index{operator, by name!emptyp (AVL, \emph{see} predicate)}
\seeonlyindex{emptyp (AVL)}{predicate}
\index{predicate, by name!emptyp (AVL tree)}
\index{operator, by name!iskeyp (AVL, \emph{see} predicate)}
\seeonlyindex{iskeyp (AVL)}{predicate}
\index{predicate, by name!iskeyp (AVL tree)}
\index{AVL tree!iskeyp predicate}
\index{operator, by name!keyp (AVL, \emph{see} predicate)}
\seeonlyindex{keyp (AVL)}{predicate}
\index{predicate, by name!keyp (AVL, occurs in)}
\index{AVL tree!keyp predicate}
\index{occurs in search tree!keyp (\emph{see} predicate)}
\index{operator, by name!treep (AVL, \emph{see} predicate)}
\seeonlyindex{treep (AVL)}{predicate}
\index{predicate, by name!treep (AVL)}
\index{AVL tree!treep predicate}
\seeonlyindex{treep (AVL)}{predicate}
\caption{Search tree operators and predicates.}
\label{fig:tree-functions}
\end{figure}
The only tree of height zero is the empty tree because
the height of a nonempty tree is one more than the maximum
of two other numbers, which makes it at least one.
Theorem \{\emph{ht-emp}\} states this fact more rigorously. \vspace{1mm}
\label{thm:ht-emp}\index{theorem, by name!\{ht-emp\}}Theorem \{\emph{ht-emp}\}: \textsf{(treep $s$)} $\rightarrow$ ((\textsf{(height $s$) = 0)} $=$ \textsf{(emptyp $s$)})
\section{Ordered Search Trees}
Since keys are natural numbers,
search trees are ordered (page \pageref{ordered-def}) if, for each node,
its key is greater than all of the keys that occur in the left subtree
and less than all the keys that occur in the right subtree.
An empty tree is ordered by default.
The following equations define
the predicate \textsf{ordp} so that
\textsf{(ordp $s$)} is true if $s$ is ordered and false otherwise:
\begin{center}
\label{def:ordp}
\index{predicate, by name!ordp (AVL tree)}\index{AVL tree!ordp predicate}
\seeonlyindex{ordp (AVL tree)}{predicate}
\index{order!AVL tree}\index{AVL tree!order}\index{search tree!order}
\index{equation, by name!\{ord\}}\index{axiom, by name!\{ord\}}
\begin{tabular}{lll}
\textsf{(ordp $s$)} $=$ & \textsf{(emptyp $s$)} $\vee$ & \{\emph{ord}\} \\
& (\textsf{(treep $s$)} $\wedge$ \\
& ~~($\forall x$.(\textsf{(keyp $x$ (lft $s$)}) $\rightarrow$ $x$ $<$ \textsf{(key $s$)})) $\wedge$ \textsf{(ordp (lft $s$))} $\wedge$ & \\
& ~~($\forall y$.(\textsf{(keyp $y$ (rgt $s$))} $\rightarrow$ $y$ $>$ \textsf{(key $s$)})) $\wedge$ \textsf{(ordp (rgt $s$))}) & \\
\end{tabular}
\end{center}
Duplicate keys do not occur in ordered search trees.
A more rigorous statement of this fact can be based on
the following observations:\index{duplicate key, none (AVL)}\index{key!duplicates, none}
%\begin{quote}
\begin{enumerate}
\item A key at a node does not occur in either of its subtrees.
\item Any key in one subtree of a node is not equal to the key of the node
and does not occur in the other subtree.
\end{enumerate}
%\end{quote}
\label{thm:keys-unique}\index{theorem, by name!\{keys unique\}}\index{AVL tree!unique keys theorem}\index{key!unique (theorem)}
%\begin{quote}
Theorem \{\emph{keys unique}\}: \\
$($\textsf{(iskeyp $k$)} $\wedge$ \textsf{(ordp $s$)}$) \rightarrow$ \\
$(((k$ $=$ \textsf{(key $s$)}$) \rightarrow ((\neg$\textsf{(keyp $k$ (lft $s$))}$) \wedge (\neg$\textsf{(keyp $k$ (rgt $s$))}$)))$ $\wedge$ \\
\hspace*{1.5mm}$(($\textsf{(keyp $k$ (lft $s$))}$) \rightarrow ((k$ $\ne$ \textsf{(key $s$)}$) \wedge (\neg$\textsf{(keyp $k$ (rgt $s$)}))$))$ $\wedge$ \\
\hspace*{1.5mm}$(($\textsf{(keyp $k$ (rgt $s$))}$) \rightarrow ((k$ $\ne$ \textsf{(key $s$)}$) \wedge (\neg$(\textsf{keyp $k$ (lft $s$))}$))))$ \\
%\end{quote}
Stating this theorem is more complicated than proving it.
By equation \{\emph{ord}\}, if \textsf{(keyp $x$ (lft $s$))}, then $x$ $<$ \textsf{(key $s$)}.
Since $k$ $=$ \textsf{(key $s$)}, we conclude that $x \neq k$.
That is, ($k$ $=$ \textsf{(key $s$)}$) \rightarrow (\neg$\textsf{(keyp $k$ (lft $s$))}$)$.
That proves one of the implications in the theorem.
The others are as easily dispatched by citing parts
of the definition of the predicate \textsf{ordp}.
\begin{exercises}
\exer {Prove:
\textsf{(ordp $s$)} $\rightarrow ($\textsf{(keyp $k$ (lft $s$))} $\rightarrow$ (($k$ $\ne$\textsf{(key $s$)}) $\wedge (\neg$ \textsf{(keyp $k$ (rgt $s$))}$)))$.}
\exer {Prove:
\textsf{(ordp $s$)} $\rightarrow ($\textsf{(keyp $k$ (rgt $s$))} $\rightarrow$ (($k$ $\ne$\textsf{(key $s$)}) $\wedge (\neg$ \textsf{(keyp $k$ (lft $s$))}$)))$.}
\end{exercises}
\section{Balanced Search Trees}
Search trees must be ordered to make it convenient to find things.
However, order is not enough.
Trees must also be short relative to the number of items in the tree.
Otherwise, order doesn't help. %'
It can take as long, on average, to find an item
in an ordered but unbalanced tree as it would
if the data were completely unorganized.
Figure \ref{fig:unbalanced-trees} (page \pageref{fig:unbalanced-trees})
compares some extremes.
\begin{figure}
\begin{center}
\includegraphics[scale=1]{images-cmyk/unbalanced-trees}
\end{center}
\index{balance!AVL tree}\index{AVL tree!balance}\index{search tree!balance}
\index{AVL tree!unbalanced}
\caption{Balance shortens trees.}
\label{fig:unbalanced-trees}
\end{figure}
The tree of height seven in
figure \ref{fig:unbalanced-trees}
is unbalanced at every level.
A binary search on this tree would have no advantage over
looking through a pile of socks one by one.
The unbalanced tree of height four in the figure is not much better.
It has the same number of nodes in the left subtree as in the right subtree,
but all of the subtrees are maximally unbalanced, like a pile of socks.
Balance is what prevents time-consuming searches, and the unbalanced
examples in figure \ref{fig:unbalanced-trees} show how bad it can get.
A search tree that has two subtrees of the same size in every node
is balanced in terms of both size and height. The tree of height three
in figure \ref{fig:unbalanced-trees} %(page \pageref{fig:unbalanced-trees})
has this maximally balanced shape.
However, the number of steps
required to find a key in a search tree is determined by the heights of subtrees,
not by the number of nodes they contain, and
a tree can be balanced with respect to height even though some nodes contain
more keys in one subtree than the other.
The balanced tree of height four in the figure shows how this can happen.
The shape of this tree isn't symmetric at any level,
but no node has subtrees whose heights differ by more than one,
and that's good enough.
We are trying to keep the height of a tree with $n$ nodes
within a fixed percentage of $log_2(n)$,
and height balancing is sufficient to accomplish this goal.
\index{balance!AVL tree}\index{AVL tree!balance}\index{search tree!balance}
Full symmetry isn't necessary. %'
As we mentioned before (page \pageref{50pct-thm}),
the height of a balanced search tree with $n$ keys
is less than $\frac{3}{2} log_2(n)$.
That makes finding things take more
steps than the optimal case in which both subtrees of
each node have the same number of keys,
but searches are still fast.
Finding a particular key among a billion nodes
might require 45 steps instead of 30, but that's %'
still plenty fast compared with half a billion steps,
on average, for unorganized data.
What we get for a few extra steps in finding things is
an astonishing improvement in the number of steps required
to insert a new key or delete an old key.
Instead of $n/2$ steps, on average,
when keys beyond the point of insertion
all need to be moved to make space for the new one,
insertion can be done in logarithmic time.
That is, the number of steps required to insert
a node in a search tree will be proportional to $log_2(n)$,
giving us the same advantage in insertion speed
that binary search provides in look-up speed.
Deletion can be handled in a similar way, and with the same effectiveness.
That is, search, insertion, and deletion can all be done in logarithmic time.
\begin{exercises}
\exer {\index{height!AVL tree}\index{AVL tree!height operator}%
\index{binary tree!height}\index{tree!height}Find a way to put $2^n - 1$ keys
in a binary search tree of height $n$.}
\end{exercises}
\section{Inserting a New Item in a Search Tree}
To make search, insertion, and deletion efficient,
search trees must be both ordered and balanced.
With regard to balance,
we must make sure that in every node the heights of the left and right subtrees
differ by one or less.
\label{balance-defun}The predicate \textsf{balp} expresses this notion formally
(figure \ref{fig:balance-defun}). %, page \pageref{fig:balance-defun}).
\begin{figure}
\begin{center}
\begin{code}
\begin{verbatim}
(defun balp (s) ; tree s is balanced?
(or (emptyp s)
(and (<= (abs (- (height (lft s)) (height (rgt s)))) 1)
(balp (lft s)) (balp (rgt s)))))
\end{verbatim}
\end{code}
\end{center}
\index{balance!AVL tree}\index{AVL tree!balance}\index{search tree!balance}\index{predicate, by name!balp (AVL balance)}\seeonlyindex{balp, AVL tree}{predicate}\index{operator, by name!balp (predicate)}
\caption{Balance predicate.}
\label{fig:balance-defun}
\end{figure}
It isn't difficult to maintain order.
You can do this by moving left or right down the tree according
to whether the new key is less than or greater than the key at the node
under consideration.
When you arrive at an empty tree,
insert a node that has the new key
with its associated data
and has empty trees as its left and right subtrees.
The new tree will be properly ordered because
of the way the procedure located a place to
hook the new key on the tree,
but the new tree will not be balanced if
the location of the new key increases the height
of a subtree that was already taller than its sibling.
Figure \ref{fig:hook-defun} (page \pageref{fig:hook-defun}) provides
an inductive definition of this insertion method.
The definition uses a noninductive formula
to put the new key into an empty tree
and an inductive formula for the nonempty case.\footnote{In
\label{same-key-new-data}
case the \textsf{hook} operator encounters a key that is the same
as the one it is inserting, it delivers a tree with that key, as it should.
However, the data associated with the new key will be the data supplied
as the second operand in the invocation of \textsf{hook}.
The old data is lost.
This provides a way to associate new data with a key.
The definition might have chosen a different alternative,
but this is a viable one for our purposes.}
\begin{figure}
\begin{center}
\begin{code}
\begin{verbatim}
(defun hook (x a s) ; put a new key x with data a into tree s
(if (empty s) ; preserve order, but not necessarily balance
(mktr x a nil nil) ; {hook0}
(let* ((k (key s)) (d (dat s))
(lf (lft s)) (rt (rgt s)))
(if (< x k)
(mktr k d (hook x a lf) rt) ; {hook<}
(if (> x k)
(mktr k d lf (hook x a rt)) ; {hook>}
(mktr x a lf rt)))))) ; {hook=}
\end{verbatim}
\end{code}
\end{center}
\index{operator, by name!hook (AVL insert key)}
\seeonlyindex{hook (AVL insertion)}{operator}
\index{AVL tree!hook (insert key)}\index{axiom, by name!\{hook0\}, \{hook$<$\}, \{hook$>$\}, \{hook$=$\}}\index{equation, by name!\{hook0\}, \{hook$<$\}, \{hook$>$\}, \{hook$=$\}}
\caption{Insert new key, preserving order but not balance.}
\label{fig:hook-defun}
\end{figure}
Inserting a new key in this way
can throw the tree out of balance.
That happens when
placement of the new key increases the height of a subtree that was
already taller than its sibling.
Then the subtree is two units taller than its sibling,
making the tree unbalanced.
In this case, a rearrangement brings it back into balance
without getting keys out of order.
\begin{aside}{aside:insertion-example}{Inserting New Nodes in Small Trees}
The following example starts with a tree containing one item,
then inserts three new items, one at a time.
We use the formula \textsf{(ins $x$ $a$ $s$)} to denote
the tree produced by inserting the key $x$ and associated
data $a$ into the search tree $s$.
The end result is an ordered,
balanced tree containing four items.
It will aid your understanding
of the insertion process if you draw diagrams similar to figure
\ref{fig:unbalanced-trees} (page \pageref{fig:unbalanced-trees})
for the trees denoted by the formulas in the example.
Verify, as you go, that each tree is both ordered and balanced.
\begin{center}
\begin{tabbing}
%
\vspace*{-1.5\topsep}
\rule{\textwidth}{0.5pt}
\vspace*{-\topsep}
%
\\
\textsf{(ins} \= \textsf{1125 "Modem" }\\
\> \textsf{{[}8444 "Audio Card" nil nil{]})} \\
\> $\Downarrow$ \\
\textsf{{[}1125 "Modem" nil {[}8444 "Audio Card" nil nil{]}{]}} \\
%
\vspace*{-1.5\topsep}
\rule{\textwidth}{0.5pt}%
\vspace*{-\topsep}
%
\\
\textsf{(ins} \= \textsf{4878 "Mouse"} \\
\> \textsf{{[}1125 "Modem" nil {[}8444 "Audio Card" nil nil{]}{]})} \\
\> $\Downarrow$ \\
\textsf{{[}4878 "Mouse"} \= \textsf{{[}1125 "Modem" nil nil{]}} \\
\> \textsf{{[}8444 "Audio Card" nil nil{]}{]}} \\
%
\vspace*{-1.5\topsep}
\rule{\textwidth}{0.5pt}%
\vspace*{-\topsep}
%
\\
\textsf{(ins} \= \textsf{2088 "Laser Jet"} \\
\> \textsf{{[}4878 "Mouse"} \= \textsf{{[}1125 "Modem" nil nil{]}} \\
\> \> \textsf{{[}8444 "Audio Card" nil nil{]}{]})} \\
\> $\Downarrow$ \\
\textsf{{[}2088 "Laser Jet"} \= \textsf{{[}1125 "Modem" nil nil{]}} \\
\> \textsf{{[}4878 "Mouse" nil {[}8444 "Audio Card" nil nil{]}{]}{]}} \\
%
\vspace*{-1.5\topsep}
\rule{\textwidth}{0.5pt}%
\vspace*{-\topsep}
%
\end{tabbing}
\end{center}\index{AVL tree!ins (insert key) examples}
%\caption{Inserting New Nodes in Small Trees}
%\label{aside:insertion-example}
\end{aside}
In small trees, it's easy to find an ad hoc rearrangement that works, %'
as illustrated in box~\ref{aside:insertion-example}
(page \pageref{aside:insertion-example}),
but we need a procedure that works for all search trees,
not just the small ones where it's easy to see what to do. %'
Figuring out a rearrangement procedure is what the rest
of this chapter is mostly about.
Putting the new node at the bottom may make the tree taller
but not necessarily.
For example, the insertion point might be on the
empty side of a node that has a tree of height one on the other side,
in which case the insertion would leave the height of the tree unchanged.
But, if the height of the tree changes,
how much could it change?
Not by more than one
(theorem \{\emph{i-ht}\}, exercise \ref{thm:i-ht}, below). %page \pageref{thm:i-ht}).
If the tree with the new key is taller than the old tree,
the new tree could be unbalanced.
However, because a height of the left subtree of a
balanced tree does not differ from the height of
the right subtree by more than one
and because the insertion of a new node cannot increase
the height of either subtree by more than one,
the heights of the left and right subtrees in the new tree
cannot differ by more than two.
So, if we can figure out how to rebalance trees where
one subtree is two units taller than its sibling,
we will have found a way to preserve balance
while inserting a new node.
\begin{exercises}
\exer {Given any three distinct keys,
there is only one search tree that is ordered, balanced,
and contains those three keys but no others.
Explain why.}
\exer {Box~\ref{aside:insertion-example}
(page \pageref{aside:insertion-example}) displays
insertions leading to an ordered, balanced search tree
containing four items.
The trees resulting from the insertions
were, in each case, chosen from some equally suitable alternatives.
Write formulas for ordered, balanced trees different from
the ones in the example but still containing the same keys.}
\exer {Use induction on height to prove the following theorem
(\textsf{ordp} is defined on page \pageref{def:ordp}):
\begin{center}\index{theorem, by name!\{i-ord\}, AVL ordered after insert}\index{order!AVL insert}
\label{thm:i-ord}
Theorem \{\emph{i-ord}\}:
\textsf{(ordp $s$)} $\rightarrow$ \textsf{(ordp (hook $k$ $a$ $s$))}
\end{center}
}
\exer {Prove by induction on tree height
that insertion of a new node does not increase height by more than one.
That is, assuming that $x$ is a key, $s$ is a search tree,
and \textsf{hook} is the operator defined in
figure \ref{fig:hook-defun} (page \pageref{fig:hook-defun}),
prove the following theorem:
\begin{center}\index{theorem, by name!\{i-ht\}, AVL height after insert}\index{balance!after AVL insert}\index{search tree!balance}\index{tree!balance}\index{height!maximum change in}
\label{thm:i-ht}
Theorem \{\emph{i-ht}\}: \textsf{(height (hook $x$ $a$ $s$))$\leq$(height $s$) $+$ $1$}
\end{center}
}%
\end{exercises}
\section{Insertion, Case by Case}
Balancing small trees is easy because there are only a few
possibilities to consider. Search trees of height two or less
are always balanced.
\begin{center}
\label{thm:bal-ht2}
\index{theorem, by name!\{bal-ht2\}, AVL balanced if ht $\le\ 2$}
Theorem \{\emph{bal-ht2}\}: \textsf{$($(height $s$) $\leq 2)$ $\rightarrow$ (balp $s$)}
\end{center}
Working through all the possibilities, one by one,
leads to a proof of this theorem.
A tree of height zero is empty
(theorem \{\emph{ht-emp}\}, page \pageref{thm:ht-emp}),
and \textsf{(balp nil)} $=$ \textsf{(emptyp nil)} is true, by definition
(figure \ref{fig:balance-defun}, page \pageref{fig:balance-defun}).
Any tree of height one will consist of a single node,
\textsf{[$k$ $d$ nil nil]}, which is balanced because both subtrees
have the same height (namely, zero).
The formula for a tree of height two must match one of the following templates:
\textsf{[$k$ $d$ [$j$ $c$ nil nil] nil]}, \textsf{[$k$ $d$ nil [$i$ $b$ nil nil]]},
or \textsf{[$k$ $d$ [$j$ $c$ nil nil] [$i$ $b$ nil nil]]}.
Applying the predicate \textsf{balp}
confirms that all of these trees are balanced,
and that completes the proof.
With big trees, there are more possibilities,
but we can reduce part of the problem to
a shorter tree, rely on induction to deal with that tree,
and use the solution produced on the shorter tree
to put together a full solution.
We want to define an insertion operator, \textsf{ins},
to put a new key in
an ordered, balanced search tree,
producing a new search tree that is ordered, balanced,
and contains the new key as well as all of the old ones.
The operator \textsf{hook} (figure \ref{fig:hook-defun}, page \pageref{fig:hook-defun})
does the job for trees of height zero or one.
The new tree is ordered (theorem \{\emph{i-ord}\}, page \pageref{thm:i-ord})
and being of height two or less, it is also balanced
(theorem \{\emph{i-ht}\}, page \pageref{thm:i-ht},
together with theorem \{\emph{bal-ht2}\}, page \pageref{thm:bal-ht2}).
So, for trees of height zero or one,
the \textsf{hook} operator, by itself, is adequate for
inserting new keys.
That leaves us with trees of height two or more.
We want to define an insertion operator, \textsf{ins},
so that
if $s$ is an ordered, balanced search tree
of height $n+2$ (where $n$ is a natural number),
then the tree \textsf{(ins $x$ $a$ $s$)}
is ordered, balanced, contains all the keys in $s$,
contains the key $x$,
contains no keys other than $x$ and those in $s$,
and has height $n+2$ or $n+3$.
To do this,
we will start with the \textsf{hook} procedure
(which has already the order and height properties
that we need for \textsf{ins}),
then find ways to rebalance when it produces
a tree with subtrees whose heights differ by more than one.\footnote{Our
primary concerns will be the issues of height and balance.
The other issues
(presence of the new key, preservation of all the old keys,
and so on)
are easy to work through from the definitions.
An issue that we will gloss over throughout the discussion
is the treatment of data associated with a key.
We include the data in the operator definitions because,
as a practical matter, search trees need some way to
associate data with keys.
Usually, keys just provide a way to find the data.
The operator definitions keep each data item
with its associated key.
Whenever we use \textsf{mktr} to build a tree,
we put the key in the first operand
and the associated data in the second operand.
This keeps the key with its data.
However, that is pretty much the extent of
our analysis of key/data associations.
Doing more is tricky because there are no
constraints on the domain of the data.
The data could even come from a domain
that doesn't support reasoning about equality.
This would be the case, for example, if the data items
were themselves operators and the search tree
were being used to provide organized access to those
operators. There is no algorithm
for determining, in general, whether two operators
denote the same operation,
so it would be difficult to reason about whether or
not key/data associations stay the same throughout the process.}
Our inductive definition of \textsf{ins} will
assume that it has the desired properties
when operating on trees of height less than $n+2$
and prove that, with that assumption, it also
has those properties on trees of height $n+2$.
\begin{figure}
\begin{center}
\includegraphics[scale=1]{images-cmyk/ht2-or-more}
\end{center}
\index{tree!balance}\index{search tree!balance}
\index{AVL tree!balance}\index{balance!AVL tree}
\caption{Balanced trees of height $n+2$.}