Design Patterns for SelfBalancing Trees Dung Zung Nguyen
Design Patterns for Self-Balancing Trees Dung “Zung” Nguyen Stephen Wong Rice University
Resources http: //www. owlnet. rice. edu/~swong/Teach. Java (http: //www. owlnet. rice. edu/~swong/Teach. Java/ NTree/presentation/NTree. ppt) http: //www. exciton. cs. rice. edu/research/OOPSLA 02
Motivations Classic self-balancing tree structures – 2 -3 -4 tree (see next slide) – red-black tree (binary tree equivalent of 2 -3 -4 tree) – B-tree (generalized 2 -3 -4 tree) – Difficult and complex. Where’s the code? What’s the proper abstraction? – Need to decouple algorithms from data structures.
A 2 -3 -4 Tree is… n. Empty n 0 -State: no data element + no sub-trees. n. Non-Empty, in 3 possible states: A n 1 -State: 1 data element + 2 sub-trees. n 2 -State: 2 data elements + 3 sub-trees. n 3 -State: 3 data elements + 4 sub-trees. A B C
Variant vs. Invariant Operations Self-balancing insertion is not an intrinsic (invariant) operation of a tree. What are the invariant operations? – Gettors & Constructors. – Constructive and Destructive operations: Constructive: Splice a tree into another. Destructive: Split a tree into a 2 -state.
Splittin’ and Splicin’ Splice: Split Up: A A B CC A B C
Structural Operations t 1. split. Up. At(1) t 1 -20 -10 10 20 -20 0 10 20 t 2 α β t 1. split. Down. At(1) -10 t 1. splice. At(1, t 2) γ -20 α β γ -10 0 10 20
Con/De-struction t 1. split. Up. At(0) 10 10 t 2 t 1. split. Down. At(0) t 2. splice. At(0, t 1) 10
Visitor Design Pattern AHost execute(v) Host 1 Host 2 Host 3 AVisitor +case 1() +case 2() +case 3() Visitor. A Visitor. B Invariant: Hosti calls casei of the visitor. Fixed # of methods fixed # of hosts
Generalized Visitors AHost execute(v) AVisitor +case. At(int i) Host 1 Host 2 Host 3 Visitor. A Visitor. B Invariant: Hosti calls case. At(i) of the visitor.
Tree. N and Algorithms
to. String() Algorithm public class To. String. Algo implements ITree. NAlgo { // Constructors omitted Empty public Object case. At(int idx, Tree. N host, Object key) {Tree switch(idx) { case 0: { return "[ ]"; } Non-Empty Tree default: { String s. Data= "", s. Trees=""; “Prefix” data for(int i = 0; i<idx; i++) { s. Data += host. get. Dat(i)+" "; s. Trees += host. get. Child(i). execute(to. String. Help, "| ")+"n"; } s. Trees += host. get. Child(idx). execute(to. String. Help, " "). to. String(); return s. Data +"n"+s. Trees; } } } ITree. NAlgo to. String. Help = …see next slide…. }
To. String() Helper private final static ITree. NAlgo to. String. Help = new ITree. NAlgo() { public Object case. At(int idx, Tree. N host, Object prefix)Tree { Empty switch(idx) { case 0: { return "|_[ ]"; } Non-Empty Tree default: { String s. Data= "", s. Trees=""; for(int i = 0; i<idx; i++) { “Prefix” data s. Data += host. get. Dat(i)+" "; s. Trees += prefix + (String) host. get. Child(i). execute(this, prefix+"| ")+"n"; } s. Trees += prefix + host. get. Child(idx). execute(this, prefix+" " ). to. String(); return "|_ "+s. Data +"n"+s. Trees; } };
Vertical Data Transport Split-down Collapse/Splice -20 -10 0 10 -20 20 -10 0 10 20 -10 0 5 10 20 5 5 Split up Splice No net height change except at root and leaves!
Command Design Pattern invokes Invoker ICommand +Object apply(Object inp) Command 1 Command 2 Performs a task. No specified semantic! Command 3 Well-defined, but unrelated semantics.
Insertion Heuristics Insertion must take place at the leaf. Tree must grow only at the root. Must transport data from the leaves to the root without affecting the height balance.
Insertion Algorithm Find insertion point at the leaf and splice new data in. Use Split-and-Apply visitor to transport excess data upwards. – Visitor passed as parameter to recursive call. – Non-root: split-and-splice – Root node: split-and-no-op will cause entire tree to grow in height. – Abstract the splice/no-op as a command passed to the visitor!
Deletion Heuristics Deletion only well-defined at leaf. Data might exist anywhere in the tree. Tree can only shorten at root. Push “candidate” data down from the root to the leaves. Bubble “excess” data back to the root. Must transport data from the root to the leaves and from the leaves to the root without affecting the height balance.
Deletion Algorithm Identify candidate data – split down at candidate and collapse with children. – If root is a 2 -node, then tree will shorten. Data to delete will appear as 2 -node below leaves. Use Split-and-Apply to transport excess data upwards.
Conclusions Proper abstraction leads to – Decoupling – Simplicity – Flexibility & extensibility Generalized Visitors open up new possibilities. Self-balancing trees teach – Abstract decomposition – Design patterns – Component-frameworks – Lambda calculus – Proof-of-correctness & complexity analysis
- Slides: 20