Solution: rank
rank searches for the key, and on the way down it counts the keys it passes that are smaller. At a node, there are three cases:
- If
keyis smaller than the node’s key, every smaller key in this subtree is in the left subtree. The rank is the rank in the left subtree. - If
keyis larger, the whole left subtree and the node itself are smaller thankey. That issize(node.left) + 1keys, plus the rank in the right subtree. - If
keyequals the node’s key, the smaller keys are exactly the left subtree.
Reaching a null child means there is nothing more to count.
@Override
public int rank(K key) {
if (key == null) {
throw new IllegalArgumentException();
}
return rank(root, key);
}
// Returns the number of keys smaller than key in the subtree rooted at node.
private int rank(Node<K, V> node, K key) {
if (node == null) {
return 0;
}
int cmp = key.compareTo(node.key);
if (cmp < 0) {
return rank(node.left, key);
} else if (cmp > 0) {
return size(node.left) + 1 + rank(node.right, key);
} else {
return size(node.left);
}
}
The key does not have to be in the map. If it is missing, the search ends at null, which returns . By then the sum already includes every smaller key the search passed on the way down.
Tracing rank(72)
At the root 50: , so 50 and its left subtree of size are smaller. That is so far, and we go right. At 70: , so 70 and its left subtree of size add , for , and we go right. At 80: , so we go left without counting. At 75: , so we go left again and reach null. The visited nodes are filled:
╭──────╮
│█50:9█│
╰───┬──╯
┌───────────┴───────────┐
╭───┴──╮ ╭───┴──╮
│ 30:3 │ │█70:5█│
╰───┬──╯ ╰───┬──╯
┌────┴────┐ ┌───────┴───────┐
╭───┴──╮ ╭───┴──╮ ╭───┴──╮ ╭───┴──╮
│ 20:1 │ │ 40:1 │ │ 60:1 │ │█80:3█│
╰──────╯ ╰──────╯ ╰──────╯ ╰───┬──╯
┌────┴────┐
╭───┴──╮ ╭───┴──╮
│█75:1█│ │ 90:1 │
╰──────╯ ╰──────╯
rank(72) returns : the keys 20, 30, 40, 50, 60, and 70.
Cost
Like select, rank is , which is on an AVL tree.
The two queries are inverses. For a stored key, select(rank(key) + 1) is that key, and for every k from to size(), rank(select(k)) is k - 1.