AuD
Lecture 'Algorithmen und Datenstrukturen' (code examples)
BinarySearchTree.java
Go to the documentation of this file.
1package aud;
2
6
137public class BinarySearchTree<Key extends Comparable<Key>,Value>
138 implements Iterable<BinarySearchTree<Key,Value>.Cursor>,
140
141
145 public class Entry {
146 final Key key_;
147 Value value_;
148
150 public Entry(Key key,Value value) {
151 key_=key;
152 value_=value;
153 }
155 public Key getKey() { return key_; }
157 public Value getValue() { return value_; }
159 public void setValue(Value value) { value_=value; }
160
161 @Override public String toString() {
162 return key_!=null ?
163 (key_.toString()+
164 (value_!=null? "=>"+value_.toString():"")) : "head";
165 }
166 }
167
168
169
174 protected class Node extends BinaryTree<Entry> {
175
176 BinarySearchTree<Key,Value> tree_ = null;
177
179 Node(BinarySearchTree<Key,Value> tree,Key key,Value value) {
180 super(new Entry(key,value));
181 tree_=tree;
182 }
183
185 public Key getKey() { return getData().getKey(); }
187 public Value getValue() { return getData().getValue(); }
189 public void setValue(Value value) { getData().setValue(value); }
190
191 @Override public GraphvizDecorator getDecorator() {
192 return tree_.getDecorator();
193 }
194
208 Node next() {
209 if (getRight()!=null) { // right -> descend left
210 Node node=(Node) getRight();
211 while (node.getLeft()!=null) { node=(Node) node.getLeft(); }
212 return node;
213 }
214
215 Node node=(Node) getParent(); // ascend up ( -> right -> descend left )
216 while (node!=head_ && compareKeys(node.getKey(),getKey())<0) {
217 Node right=(Node) node.getRight();
218 if (right!=null && right!=this &&
219 compareKeys(right.getKey(),getKey())>=0) {
220 node=right;
221 while (node.getLeft()!=null) { node=(Node) node.getLeft(); }
222 return node;
223 }
224 node=(Node) node.getParent();
225 }
226 return node!=head_ ? node : null;
227 }
228 }
229
230 protected Node createNode(Key key,Value value) {
231 return new Node(this,key,value);
232 }
233
234 protected Node head_;
235
237 public BinarySearchTree() {
238 head_ = createNode(null,null);
239 }
240
242 public boolean isEmpty() {
243 return head_.getRight()==null;
244 }
245
250 protected int compareKeys(Key a,Key b) {
251 if (a==null) return -1;
252 if (b==null) return +1;
253 else return a.compareTo(b);
254 }
255
262 protected class LowerBound {
264 protected Node node;
266 protected Node parent;
268 protected int cmp;
269
271 this.node=node;
272 this.parent=parent;
273 this.cmp=cmp;
274 }
278 Value getValue() {
279 return node!=null ? node.getValue() : null;
280 }
281 }
282
308 protected LowerBound findLowerBound(Key key) {
309 Node node=(Node) head_.getRight(), parent=head_;
310 int cmp=+1; // always larger than head_
311
312 while (node!=null &&
313 (cmp=compareKeys(key,node.getKey()))!=0) {
314 parent=node;
315 if (cmp<0)
316 node=(Node) node.getLeft();
317 else
318 node=(Node) node.getRight();
319 }
320 return new LowerBound(node,parent,cmp);
321 }
322
326 public Value find(Key key) {
327 return findLowerBound(key).getValue();
328 }
329
333 public class Cursor {
334 final Node node_;
335
336 Cursor(Node node) { assert(node!=head_); node_=node; }
337
339 public Key getKey() { return node_.getKey(); }
341 public Value getValue() { return node_.getValue(); }
343 public void setValue(Value value) { node_.setValue(value); }
344 }
345
347 public Cursor findEntry(Key key) {
348 Node node=findLowerBound(key).node;
349 return node!=null ? new Cursor(node) : null;
350 }
351
355 public Cursor getMinimum() {
356 Node node=(Node) head_.getRight(), parent=head_;
357 while (node!=null) {
358 parent=node;
359 node=(Node) node.getLeft();
360 }
361 return parent!=head_ ? new Cursor(parent) : null;
362 }
363
367 public Cursor getMaximum() {
368 Node node=(Node) head_.getRight(), parent=head_;
369 while (node!=null) {
370 parent=node;
371 node=(Node) node.getRight();
372 }
373 return parent!=head_ ? new Cursor(parent) : null;
374 }
375
377 public static abstract class Visitor {
378 abstract public void visit(String state,
379 String key, String value, String tikz);
380 };
381
383 public void visitPreorder(Visitor visitor) {
384 visitPreorder(visitor, (Node) head_.getRight());
385 }
386
387 void visitPreorder(Visitor visitor, Node node) {
388 visitor.visit("node-pre", null, null, null);
389
390 if (node != null) {
391 Value val = node.getValue();
392 visitor.visit("node", node.getKey().toString(),
393 val != null ? val.toString() : null,
394 node.tikzNodeStyle());
395
396 visitor.visit("left-down", null, null, null);
397 visitPreorder(visitor, (Node) node.getLeft());
398 visitor.visit("left-up", null, null, null);
399
400 visitor.visit("right-down", null, null, null);
401 visitPreorder(visitor, (Node) node.getRight());
402 visitor.visit("right-up", null, null, null);
403 }
404
405 visitor.visit("node-post", null, null, null);
406 }
407
411 public class Iterator implements java.util.Iterator<Cursor> {
412 java.util.Iterator<BinaryTree<Entry> > iter_;
413
415 (java.util.Iterator<BinaryTree<Entry> >iter) {
416 iter_=iter;
417 }
418
419 @Override public boolean hasNext() {
420 return iter_!=null && iter_.hasNext();
421 }
422
423 @Override public Cursor next() {
424 Node node=(Node) iter_.next();
425 return new Cursor(node);
426 }
430 @Override public void remove() {
431 throw new UnsupportedOperationException();
432 }
433 }
434
436 @Override public Iterator iterator() {
437 return new Iterator
438 (isEmpty() ? null : head_.getRight().inorder().iterator());
439 }
440
441
459 public Range range(Key begin,Key end) {
460 if (isEmpty() || (end!=null && compareKeys(begin,end)>=0))
461 return new Range(null,null);
462
463 LowerBound lower=findLowerBound(begin);
464 Node start=lower.node!=null ? lower.node : lower.parent;
465 Node stop =null;
466 if (end!=null) {
467 lower=findLowerBound(end);
468 stop=lower.node!=null ? lower.node : lower.parent;
469 }
470 return new Range(start,stop);
471 }
472
474 public class Range implements Iterable<Cursor> {
475 final Node begin_;
476 final Node end_;
477
478 Range(Node begin,Node end) {
479 begin_=begin;
480 end_=end;
481 }
482
483 @Override public RangeIterator iterator() {
484 return new RangeIterator(this);
485 }
486 }
487
491 public class RangeIterator implements java.util.Iterator<Cursor> {
492 Range range_;
493 Node node_;
494
495 RangeIterator(Range range) {
496 range_=range;
497 node_=range_.begin_;
498 }
499
500 @Override public boolean hasNext() {
501 return node_!=range_.end_;
502 }
503
504 @Override public Cursor next() {
505 assert(node_!=null);
506 Node cur=node_;
507 node_=node_.next();
508 return new Cursor(cur);
509 }
513 @Override public void remove() {
514 throw new UnsupportedOperationException();
515 }
516 }
517
534 public Value insert(Key key,Value value) {
535 LowerBound r=findLowerBound(key);
536
537 if (r.node!=null) { // key exists in tree
538 Value old=r.node.getValue();
539 r.node.setValue(value);
540 return old;
541 }
542 assert(r.cmp!=0);
543 assert(r.parent!=null);
544
545 Node node=createNode(key,value);
546 Node old;
547
548 if (r.cmp<0)
549 old=(Node) r.parent.setLeft(node);
550 else
551 old=(Node) r.parent.setRight(node);
552
553 onInsert(node); // notify via callback
554
555 assert(old==null);
556
557 return null;
558 }
559
573 protected void onInsert(Node node) {}
574
575
582 public Value remove(Key key) {
583 LowerBound r=findLowerBound(key);
584 Node node=r.node;
585
586 if (node==null)
587 return null; // no such node
588
589 Value value=node.getValue();
590
591 removeNode(node);
592
593 return value;
594 }
595
599 protected void removeNode(Node node) {
600 Node parent=(Node) node.getParent();
601 Node child; // update to parent
602
603 assert(parent!=null);
604 assert(parent.getLeft()==node || parent.getRight()==node);
605
606 if (node.isLeaf()) // trivial case
607 child=null;
608 else if (node.getLeft()==null) // 1 child
609 child=(Node) node.getRight();
610 else if (node.getRight()==null) // 1 child
611 child=(Node) node.getLeft();
612 else { // 2 children
613 child=(Node) node.getRight();
614 while (child.getLeft()!=null)
615 child=(Node) child.getLeft();
616
617 child.setLeft(node.getLeft());
618 node.left_=null; // because setXXX sets parent!
619
620 Node p=(Node) child.getParent();
621 if (p!=node) {
622 p.setLeft(child.getRight());
623 child.right_=null; // because setXXX sets parent!
624 child.setRight(node.getRight());
625 }
626 }
627
628 if (parent.getLeft()==node)
629 parent.setLeft(child);
630 else
631 parent.setRight(child);
632
633 onRemove();
634 }
635
640 protected void onRemove() {}
641
652 protected Node restructure(Node grandchild) {
653 assert(grandchild.getParent()!=head_);
654 assert(grandchild.getParent().getParent()!=head_);
655
656 Node child=(Node) grandchild.getParent();
657 Node parent=(Node) child.getParent();
658
659 // tri-node restructuring following [Goodrich andTamassia]
660
661 Node x=grandchild, y=child, z=parent;
662 boolean xLeft=(x==y.getLeft());
663 boolean yLeft=(y==z.getLeft());
664
665 Node a,b,c; // find inorder traversal of x,y,z
666 Node t0,t1,t2,t3;
667
668 if (xLeft && yLeft) {
669 a=x; b=y; c=z;
670 t0=(Node) a.getLeft();
671 t1=(Node) a.getRight();
672 t2=(Node) b.getRight();
673 t3=(Node) c.getRight();
674 }
675 else if (!xLeft && yLeft) {
676 a=y; b=x; c=z;
677 t0=(Node) a.getLeft();
678 t1=(Node) b.getLeft();
679 t2=(Node) b.getRight();
680 t3=(Node) c.getRight();
681 }
682 else if (xLeft && !yLeft) {
683 a=z; b=x; c=y;
684 t0=(Node) a.getLeft();
685 t1=(Node) b.getLeft();
686 t2=(Node) b.getRight();
687 t3=(Node) c.getRight();
688 }
689 else { // right/right
690 a=z; b=y; c=x;
691 t0=(Node) a.getLeft();
692 t1=(Node) b.getLeft();
693 t2=(Node) c.getLeft();
694 t3=(Node) c.getRight();
695 }
696
697 return setupBalancedSubtree(z,a,b,c,t0,t1,t2,t3);
698 }
699
711 protected Node setupBalancedSubtree
712 (Node z,Node a,Node b,Node c,Node t0,Node t1,Node t2,Node t3) {
713
714 Node root=(Node) z.getParent();
715 assert(root!=null);
716
717 if (root.getLeft()==z)
718 root.setLeft(b);
719 else
720 root.setRight(b);
721
722 a.left_=a.right_=null; // avoid update of their parent
723 a.setLeft(t0);
724 a.setRight(t1);
725
726 c.left_=c.right_=null;
727 c.setLeft(t2);
728 c.setRight(t3);
729
730 b.left_=b.right_=null;
731 b.setLeft(a);
732 b.setRight(c);
733
734 return b;
735 }
736
751 protected Node rotateLeft(Node node) {
752 Node a=node;
753 Node b=(Node) node.getRight(); assert(b!=null);
754 Node c=(Node) b.getRight(); assert(c!=null);
755
756 Node t0=(Node) a.getLeft();
757 Node t1=(Node) b.getLeft();
758 Node t2=(Node) c.getLeft();
759 Node t3=(Node) c.getRight();
760
761 Node z=a;
762
763 return setupBalancedSubtree(z, a,b,c, t0,t1,t2,t3);
764 }
765
780 protected Node rotateRightLeft(Node node) {
781 Node a=node;
782 Node c=(Node) node.getRight(); assert(c!=null);
783 Node b=(Node) c.getLeft(); assert(b!=null);
784
785 Node t0=(Node) a.getLeft();
786 Node t1=(Node) b.getLeft();
787 Node t2=(Node) b.getRight();
788 Node t3=(Node) c.getRight();
789
790 Node z=a;
791
792 return setupBalancedSubtree(z, a,b,c, t0,t1,t2,t3);
793 }
794
809 protected Node rotateRight(Node node) {
810 Node c=node;
811 Node b=(Node) node.getLeft(); assert(b!=null);
812 Node a=(Node) b.getLeft(); assert(a!=null);
813
814 Node t0=(Node) a.getLeft();
815 Node t1=(Node) a.getRight();
816 Node t2=(Node) b.getRight();
817 Node t3=(Node) c.getRight();
818
819 Node z=c;
820
821 return setupBalancedSubtree(z, a,b,c, t0,t1,t2,t3);
822 }
823
838 protected Node rotateLeftRight(Node node) {
839 Node c=node;
840 Node a=(Node) node.getLeft(); assert(a!=null);
841 Node b=(Node) a.getRight(); assert(b!=null);
842
843 Node t0=(Node) a.getLeft();
844 Node t1=(Node) b.getLeft();
845 Node t2=(Node) b.getRight();
846 Node t3=(Node) c.getRight();
847
848 Node z=c;
849
850 return setupBalancedSubtree(z, a,b,c, t0,t1,t2,t3);
851 }
852
865 public void restructure(char action,Key key) {
866 if (key==null)
867 throw new RuntimeException("invalid key 'null'");
868
869 Node node=findLowerBound(key).node;
870 if (node==null)
871 throw new RuntimeException("no such node '"+key+"'");
872
873 switch (action) {
874 case '/':
875 if (node.getLeft()==null || node.getLeft().getLeft()==null)
876 throw new RuntimeException("missing node->left>left");
877 decorator_.highlightNode(rotateRight(node));
878 break;
879 case '\\':
880 if (node.getRight()==null || node.getRight().getRight()==null)
881 throw new RuntimeException("missing node->left->right");
882 decorator_.highlightNode(rotateLeft(node));
883 break;
884 case '>':
885 if (node.getRight()==null || node.getRight().getLeft()==null)
886 throw new RuntimeException("missing node->right->left");
887 decorator_.highlightNode(rotateRightLeft(node));
888 break;
889 case '<':
890 if (node.getLeft()==null || node.getLeft().getRight()==null)
891 throw new RuntimeException("missing node->left->right");
892 decorator_.highlightNode(rotateLeftRight(node));
893 break;
894 case '=':
895 if (node.getParent()==head_ || node.getParent().getParent()==head_)
896 throw new RuntimeException("missing node->parent->parent");
897 decorator_.highlightNode(restructure(node));
898 }
899 }
900
902 public class Decorator extends aud.util.SimpleDecorator {
903 @Override public String getNodeDecoration(GraphvizDecorable object) {
904 return object==head_ ? "shape=ellipse" : super.getNodeDecoration(object);
905 }
906 public void highlight(Key key) {
907 highlightNode(findLowerBound(key).node);
908 }
909 public void mark(Key key) {
910 markNode(findLowerBound(key).node);
911 }
912 public void unmark(Key key) {
913 unmarkNode(findLowerBound(key).node);
914 }
915 }
916
917 Decorator decorator_ = new Decorator();
918
919 @Override public GraphvizDecorator getDecorator() {
920 return decorator_;
921 }
922
923 @Override public String toDot() {
924 return head_.toDot();
925 }
926
927 @Override public String toString() {
928 String s="";
929 if (!isEmpty()) {
930 java.util.Iterator<?> ii=head_.getRight().inorder().iterator();
931 while (ii.hasNext()) {
932 s+=ii.next();
933 if (ii.hasNext())
934 s+=",";
935 }
936 }
937 return s;
938 }
939
941 public String toText() {
942 return isEmpty() ? "" : head_.getRight().toText();
943 }
944
946 public String toTikZ() {
947 return isEmpty() ? "" : head_.getRight().toTikZ();
948 }
949
953 public void checkConsistency() {
954 if (!isEmpty())
955 checkConsistency((Node) head_.getRight());
956 }
957 protected void checkConsistency(Node node) {
958 Node parent=(Node) node.getParent();
959 assert(parent!=null);
960 assert((Node) parent.getLeft()==node || (Node) parent.getRight()==node);
961
962 if (parent==null)
963 throw new RuntimeException("parent!=null");
964 if ((Node) parent.getLeft()!=node && (Node) parent.getRight()!=node)
965 throw new RuntimeException("invalid parent");
966
967 if (node.getLeft()!=null)
968 checkConsistency((Node) node.getLeft());
969 if (node.getRight()!=null)
970 checkConsistency((Node) node.getRight());
971 }
972
974 public static void main(String[] args) {
975 BinarySearchTree<String,Object> tree=
976 new BinarySearchTree<String,Object> ();
977 System.out.println(tree);
978
979 //String[] keys={"a","b","c","d","e","f","g"};
980 //String[] keys={"d","b","f","a","c","e","g"};
981 String[] keys={"a","b","d","c","g","f","e"};
982
983
984 for (String key : keys)
985 tree.insert(key,null);
986
987 System.out.println(tree);
988 System.out.println(tree.toText()); tree.checkConsistency();
989
990 for (BinarySearchTree<String,Object>.Cursor c : tree)
991 System.out.print(c.getKey());
992 System.out.println();
993
994 for (BinarySearchTree<String,Object>.Cursor c : tree.range("b","f"))
995 System.out.print(c.getKey());
996 System.out.println();
997
998 keys=new String[]{"a","c","e","g"};
999
1000 for (String key : keys)
1001 tree.remove(key);
1002
1003 System.out.println(tree);
1004 System.out.println(tree.toText());
1005 }
1006}
Node head_
Reference to a key-value pair in a BinarySearchTree.
void setValue(Value value)
set value
enables decoration "by key"
String getNodeDecoration(GraphvizDecorable object)
get node decoration
Key-value pair as entry (= node data) in a search tree.
void setValue(Value value)
set value
Entry(Key key, Value value)
construct node
Node node
the node found or null for failed search
Node parent
node's parent (always !=null)
int cmp
result of the last call to compareKeys
Node in a BinarySearchTree.
Value getValue()
get value (short for getData().getValue())
void setValue(Value value)
set value (short for getData().setValue(value))
GraphvizDecorator getDecorator()
Key getKey()
get key (short for getData().getKey())
iterable range (subsequence)
Simple binary tree.
Definition: BinaryTree.java:26
String toDot()
Get dot representation.
BinaryTree< T > getLeft()
get left child or null
Definition: BinaryTree.java:86
T getData()
get node data
Definition: BinaryTree.java:81
BinaryTree< T > getRight()
get right child or null)
Definition: BinaryTree.java:88
BinaryTree< T > getParent()
get node's parent or null for root
Definition: BinaryTree.java:84
Decorator for items of Graphvizable objects.
void markNode(GraphvizDecorable object)
void unmarkNode(GraphvizDecorable object)
unmark node object
void highlightNode(GraphvizDecorable object)
Set highlighted node.
Interface for decorating items of Graphvizable objects.
Interface for GraphViz rendering.
utilities (not related to AuD lecture)
Definition: Colormap.java:1
AuD lecture: Data structures, algorithms, examples.
Definition: A234Tree.java:1