By the end of this module, you will be able to:
The general search problem:
Examples:
We will divide search problems into the following categories:
public interface OrderedSearchAlgorithm { public void insert (Comparable key, Object value); public Object search (Comparable key); public Object minimum (); public Object maximum (); }
public interface EqualitySearchAlgorithm { public void insert (Object key, Object value); public Object delete (Object key); public Object search (Object key); }
Algorithms vs. data structures:
Maps and Sets:
public interface Set { public void add (Object obj); public boolean search (Object obj); public void delete (Object obj); public Enumeration getEnumeration (); }
public interface Map { public void add (Comparable key, Object value); public Object search (Comparable key); public void delete (Comparable key); public Enumeration getKeyEnumeration (); public Enumeration getObjectEnumeration (); }
Preview:
In-Class Exercise 3.1:
Download MapSetExample.java
and add code:
What is a binary search tree?
Insertion:
In-Class Exercise 3.2: Write pseudocode for search, both a recursive and an iterative version.
Algorithm: insert (key, value) Input: key-value pair 1. if tree is empty 2. create new root with key-value pair; 3. return 4. endif 5. treeNode = search (key) 6. if treeNode not null // It's already there. 7. Replace old with new; 8. return 9. endif // Otherwise, use recursive insertion 10. recursiveInsert (root, key, value)
Algorithm: recursiveInsert (node, key, value) Input: a tree node (root of subtree), key-value pair 1. if key < node.key 2. if node.left not empty 3. recursiveInsert (node.left, key, value) 4. return 5. endif // Otherwise, append key here. 6. Create new node and append to node.left; // Otherwise, go right. 7. else 8. if node.right not empty 9. recursiveInsert (node.right, key, value) 10. return 11. endif // Otherwise, append key here. 12. Create new node and append to node.right; 13. endif
In-Class Exercise 3.3: Insert the following (single-character) elements into a binary tree and draw the binary tree: "A B C D E F G".
Min (and max keys)
In-Class Exercise 3.4:
Successors (and predecessors):
Deletion:
In-Class Exercise 3.5: Why is this true?
Analysis:
In-Class Exercise 3.6:
Motivation:
Key ideas:
Note: AVL = Adelson-Velskii and Landis (authors of original paper)
Rotations:
Note:
followed by
followed by
In-Class Exercise 3.7:
Perform a left rotation of node 8. Then perform a rotation of node 8
into the root position.
Insertion in an AVL tree:
Algorithm: insert (key, value) Input: key-value pair 1. if tree is empty 2. create new root with key-value pair; 3. return 4. endif // Otherwise use recursive insertion 5. root = recursiveInsert (root, key, value)
Algorithm: recursiveInsert (node, key, value) Input: subtree root (node), key-value pair // Bottom out of recursion 1. if node is empty 2. create new node with key-value pair and return it; 3. endif // Otherwise, insert on appropriate side 4. if key < node.key // First insert. 5. node.left = recursiveInsert (node.left, key, value) 6. // Then, check whether to balance. 7. if height(node.left) - height(node.right) > 1 8. if insertion occurred on left side of node.left 9. node = rotateLeftChild (node) 10. else 11. node = doubleRotateLeftChild (node) 12. endif 13. endif 14. else // Insert on right side 15. node.right = recursiveInsert (node.right, key, value) // Check balance. 16. if (height(node.right) - height(node.left) > 1 17. if insertion occurred on right side of node.right 18. node = rotateRightChild (node) 19. else 20. node = doubleRotateRightChild (node) 21. endif 22. endif 23. endif 24. recomputeHeight (node); 25. return node; Output: pointer to subtree
Search: same as in binary search tree.
Deletion: complicated (not covered here).
Analysis:
In practice:
About multiway trees:
In-Class Exercise 3.8:
Suppose a multiway tree of degree m has n keys.
Searching:
Search for `14':
Insertion:
Note:
Insertion example:
Step 2: Add new key `10':
Step 3: Insert median element `7' with left and right pointers in
level above.
⇒ in this case, create new root.
Note:
Final tree after inserting `11':
Pseudocode:
Algorithm: search (key) Input: a search key 1. Initialize stack; 2. found = false 3. recursiveSearch (root, key) 4. if found 5. node = stack.pop() 6. Extract value from node; // For insertions: 7. stack.push (node) 8. return value 9. else 10. return null 11. endif Output: value corresponding to key, if found.
Algorithm: recursiveSearch (node, key) Input: tree node, search key 1. Find i such that i-th key in node is smallest key larger than or equal to key. 2. stack.push (node) 3. if found in node 4. found = true 5. return 6. endif // If we're at a leaf, the key wasn't found. 7. if node is a leaf 8. return 9. endif // Otherwise, follow pointer down to next level 10. recursiveSearch (node.child[i], key)
Algorithm: insert (key, value) Input: key-value pair 1. if tree is empty 2. create new root and add key-value; 3. return 4. endif // Otherwise, search: stack contains path to leaf. 5. search (key) 6. if found 7. Handle duplicates; 8. return 9. endif 10. recursiveInsert (key, value, null, null)
Algorithm: recursiveInsert (key, value, left, right) Input: key-value pair, left and right pointers // First, get the node from the stack. 1. node = stack.pop() 2. if node.numEntries < 2m-1 // Space available. 3. insert in correct position in node, along with left and right pointers. 4. return 5. endif 6. // Otherwise, a split is needed. 7. medianKey = m-th key in node; 8. medianValue = m-th value in node; 9. Create newRightNode; 10. Place keys-values-pointers from 1,...,m-1 in current (left) node. 11. Place keys-values-pointers from m+1,...,2m-1 in newRightNode; 12. Put key and value in appropriate node (node or newRightNode). 13. if node == root 14. createNewRoot (medianKey, medianValue, node, newRightNode) 15. else 16. recursiveInsert (medianKey, medianValue, node, newRightNode) 17. endif
Recall the advantages of a multiway tree:
But the disadvantages:
This raises the question: is there a hybrid between a multiway tree and a binary tree?
The answer is yes, a red-black tree.
A red-black tree is really a binary-tree representation of a multiway tree of degree 2:
Insertion is the tricky operation, so let's take a closer look:
For the multiway tree:
Can we boil down the red-black cases into some simple "rules"?
In this case, rotation continues upwards.
In-Class Exercise 3.9:
Draw the red-black tree corresponding to this multiway tree:
Then, insert the value "11" and, side-by-side, show the steps in both the
multiway tree and the red-black tree.
In-Class Exercise 3.10: Argue that the number of black nodes counted along a path from the root to any leaf is the same for all leaves. Also, why is true that every red node must have a black parent?
Analysis:
What does "self-adjusting" mean?
In-Class Exercise 3.11: Suppose the frequency of access for letter "A" is fA, the frequency of access for letter "B" is fB ... etc. What is the best way to order the list?
Self-adjusting linked lists:
In-Class Exercise 3.12:
Consider the following 5-element list with initial order: "A" "B" "C" "D" "E".
About self-adjusting linked lists:
Self-adjusting binary trees:
Using simple rotations:
In-Class Exercise 3.13: Show the intermediate trees obtained in rotating "C" to the root position.
After rotating "C" to the root:
⇒ simple rotations can push "down one side".
Using splaystep operations:
Search:
Algorithm: search (key) Input: a search-key 1. found = false; 2. node = recursiveSearch (root, key) 3. if found 4. Move node to root-position using splaysteps; 5. return value 6. else 7. return null 8. endif Output: value corresponding to key, if found.
Algorithm: recursiveSearch (node, key) Input: tree node, search-key 1. if key = node.key 2. found = true 3. return node 4. endif // Otherwise, traverse further 5. if key < node.key 6. if node.left is null 7. return node 8. else 9. return recursiveSearch (node.left, key) 10. endif 11. else 12. if node.right is null 13. return node 14. else 15. return recursiveSearch (node.right, key) 16. endif 17. endif Output: pointer to node where found; if not found, pointer to node for insertion.
Insertion:
Algorithm: insert (key, value) Input: a key-value pair 1. if tree is empty 2. Create new root and insert key-value pair; 3. return 4. endif // Otherwise, use search to find appropriate position 5. node = recursiveSearch (root, key) 6. if found 7. Handle duplicates; 8. return 9. endif // Otherwise, node returned is correct place to insert. 10. Create new tree-node and insert as child of node; 11. Move newly created tree-node to root position using splaysteps;
Deletion:
Analysis:
In-Class Exercise 3.14:
Show the intermediate trees and final tree after "J" is moved into the
root position using splaysteps. In each case, identify which "CASE" is
used.
Lastly, are there other kinds of useful "tree" data structures?
Yes. Some examples (not covered in this course):