Key-value pair abstraction.
Intended consequences.
Best practices. Use immutable types for symbol table keys.
Value type. Any generic type.
Key type: several natural assumptions.
All Java classes inherit a method equals().
public final class Date implements Comparable<Date> // final??
{
private final int month;
private final int day;
private final int year;
...
public boolean equals(Object y)
{
if (y == this) return true;
if (y == null) return false;
if (y.getClass() != this.getClass()) // vs. instanceof??
return false;
Date that = (Date) y;
if (this.day != that.day ) return false;
if (this.month != that.month) return false;
if (this.year != that.year ) return false;
return true;
}
}
“Standard” recipe for user-defined types.
Best practices.
Make compareTo() consistent with equals().
x.equals(y) if and only if (x.compareTo(y) == 0)
ST<String, Integer> st = new ST<String, Integer>();
Frequency counter.
Data structure. Maintain an (unordered) linked list of key-value pairs.
Data structure. Maintain an ordered array of key-value pairs.
public Value get(Key key)
{
if (isEmpty()) return null;
int i = rank(key);
if (i < N && keys[i].compareTo(key) == 0) return vals[i];
else return null;
}
private int rank(Key key)
{
int lo = 0, hi = N-1;
while (lo <= hi)
{
int mid = lo + (hi - lo) / 2;
int cmp = key.compareTo(keys[mid]);
if (cmp < 0) hi = mid - 1;
else if (cmp > 0) lo = mid + 1;
else if (cmp == 0) return mid;
}
return lo;
}
Problem. To insert, need to shift all greater keys over.
如果 symbol table 是有序的,可以有很多方法可以提供。
public class ST<Key extends Comparable<Key>, Value>
ST() create an ordered symbol table
void put(Key key, Value val) put key-value pair into the table (remove key from table if value is null)
Value get(Key key) value paired with key (null if key is absent)
void delete(Key key) remove key (and its value) from table
boolean contains(Key key) is there a value paired with key?
boolean isEmpty() is the table empty?
int size() number of key-value pairs
Key min() smallest key
Key max() largest key
Key floor(Key key) largest key less than or equal to key
Key ceiling(Key key) smallest key greater than or equal to key
int rank(Key key) number of keys less than key
Key select(int k) key of rank k
void deleteMin() delete smallest key
void deleteMax() delete largest key
int size(Key lo, Key hi) number of keys in [lo..hi]
Iterable<Key> keys(Key lo, Key hi) keys in [lo..hi], in sorted order
Iterable<Key> keys() all keys in the table, in sorted order
Definition. A BST is a binary tree in symmetric order.
Symmetric order. Each node has a key, and every node’s key is:
BST representation in Java
private class Node
{
private Key key;
private Value val;
private Node left, right;
public Node(Key key, Value val)
{
this.key = key;
this.val = val;
}
}
Search. If less, go left; if greater, go right; if equal, search hit.
Insert. If less, go left; if greater, go right; if null, insert.
Get. Return value corresponding to given key, or null if no such key.
public Value get(Key key)
{
Node x = root;
while (x != null)
{
int cmp = key.compareTo(x.key);
if (cmp < 0) x = x.left;
else if (cmp > 0) x = x.right;
else if (cmp == 0) return x.val;
}
return null;
}
Cost. Number of compares is equal to 1 + depth of node.
Put. Associate value with key.
Search for key, then two cases:
・Key in tree ⇒ reset value.
・Key not in tree ⇒ add new node.
public void put(Key key, Value val)
{
root = put(root, key, val);
}
private Node put(Node x, Key key, Value val)
{
if (x == null) return new Node(key, val);
int cmp = key.compareTo(x.key);
if (cmp < 0)
x.left = put(x.left, key, val);
else if (cmp > 0)
x.right = put(x.right, key, val);
else if (cmp == 0)
x.val = val;
return x;
}
Cost. Number of compares is equal to 1 + depth of node.
Remark. Tree shape depends on order of insertion.
Correspondence between BSTs and quicksort partitioning
Minimum. Smallest key in table.
Maxmum. Largest key in table.
floor, ceil
public Key floor(Key key)
{
Node x = floor(root, key);
if (x == null) return null;
return x.key;
}
private Node floor(Node x, Key key)
{
if (x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) return floor(x.left, key);
Node t = floor(x.right, key);
if (t != null) return t;
else return x;
}
Subtree counts
In each node, we store the number of nodes in the subtree rooted at that node; to implement size(), return the count at the root.
private class Node
{
private Key key;
private Value val;
private Node left;
private Node right;
private int count; // number of nodes in subtree
}
public int size()
{ return size(root); }
private int size(Node x)
{
if (x == null) return 0;
return x.count;
}
private Node put(Node x, Key key, Value val)
{
if (x == null) return new Node(key, val, 1);
int cmp = key.compareTo(x.key);
if (cmp < 0) x.left = put(x.left, key, val);
else if (cmp > 0) x.right = put(x.right, key, val);
else if (cmp == 0) x.val = val;
x.count = 1 + size(x.left) + size(x.right);
return x;
}
public int rank(Key key)
{
return rank(key, root);
}
private int rank(Key key, Node x)
{
if (x == null) return 0;
int cmp = key.compareTo(x.key);
if (cmp < 0) return rank(key, x.left);
else if (cmp > 0) return 1 + size(x.left) + rank(key, x.right);
else if (cmp == 0) return size(x.left);
}
Inorder traversal
Traverse left subtree.
Enqueue key.
Traverse right subtree.
public Iterable<Key> keys()
{
Queue<Key> q = new Queue<Key>();
inorder(root, q);
return q;
}
private void inorder(Node x, Queue<Key> q)
{
if (x == null) return;
inorder(x.left, q);
q.enqueue(x.key);
inorder(x.right, q);
}
Property. Inorder traversal of a BST yields keys in ascending order.
To delete the minimum key:
Update subtree counts.
public void deleteMin()
{
root = deleteMin(root);
}
private Node deleteMin(Node x)
{
if (x.left == null) return x.right;
x.left = deleteMin(x.left);
x.count = 1 + size(x.left) + size(x.right);
return x;
}
Hibbard deletion
To delete a node with key k: search for node t containing key k.
Case 0. [0 children] Delete t by setting parent link to null.
Case 1. [1 child] Delete t by replacing parent link.
Case 2. [2 children]
・Find successor x of t.
・Delete the minimum in t’s right subtree.
・Put x in t’s spot.
public void delete(Key key)
{
root = delete(root, key);
}
private Node delete(Node x, Key key) {
if (x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp < 0) x.left = delete(x.left, key);
else if (cmp > 0) x.right = delete(x.right, key);
else {
if (x.right == null) return x.left;
if (x.left == null) return x.right;
Node t = x;
x = min(t.right);
x.right = deleteMin(t.right);
x.left = t.left;
}
x.count = size(x.left) + size(x.right) + 1;
return x;
}
Surprising consequence. Trees not random (!) ⇒ sqrt (N) per op.
Next lecture. Guarantee logarithmic performance for all operations.