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):