Patricia Tries
CMSC 420
Acronyms in Computer Science
• PATRICIA stands for Practical Algorithm to Retrieve Information
Coded In Alphanumeric
Acronyms in Computer Science
• PATRICIA stands for Practical Algorithm to Retrieve Information
Coded In Alphanumeric
• Pop quiz: What was Donald R. Morrison, inventor of PATRICIA tries
back in 1968, smoking when he came up with this horrendous name?
Weed
Crack
LSD
Meth
Acronyms in Computer Science
• PATRICIA stands for Practical Algorithm to Retrieve Information
Coded In Alphanumeric
• Pop quiz: What was Donald R. Morrison, inventor of PATRICIA tries
back in 1968, smoking when he came up with this horrendous name?
Weed
Crack
LSD
Meth
• Speaking of acronyms, what does DEBIAN stand for?
Acronyms in Computer Science
• PATRICIA stands for Practical Algorithm to Retrieve Information
Coded In Alphanumeric
• Pop quiz: What was Donald R. Morrison, inventor of PATRICIA tries
back in 1968, smoking when he came up with this horrendous name?
Weed
Crack
LSD
Meth
• Speaking of acronyms, what does DEBIAN stand for?
• The late Ian Murdock, founder of the project, and his ex-wife,
Deborah!
Problems with tries
• “Non-key” nodes with just one (non-null) child are about as useful as
a Greek State Employee.
Source: Google Image Search
for «δημόσιος υπάλληλος»,
which is Greek for “state
employee”.
Problems with tries
• “Non-key” nodes with just one (non-null) child are about as useful as
a Greek State Employee.
• Look at this example of a trie with two long keys, and two not so long
ones, but which do share a prefix:
root
s
a
r
c
p
k
n
i
d
e
o
i
o
f
n
Problems with tries
• “Non-key” nodes with just one (non-null) child are about as useful as
a Greek State Employee.
• Look at this example of a trie with two long keys, and two not so long
ones, but which do share a prefix:
root
s
a
r
c
p
k
n
i
d
e
o
i
o
f
n
The problem stems
from the fact that
every link is
associated with a
character…
Problems with tries
• “Non-key” nodes with just one (non-null) child are about as useful as
a Greek State Employee.
• Look at this example of a trie with two long keys, and two not so long
ones, but which do share a prefix:
root
root
s
a
r
c
p
k
n
i
d
e
o
i
o
f
n
But if we were somehow able
to associate entire paths with
a string, compressing links…
d
c
ar
id
Problems with tries
• “Non-key” nodes with just one (non-null) child are about as useful as
a Greek State Employee.
• Look at this example of a trie with two long keys, and two not so long
ones, but which do share a prefix:
root
root
s
a
r
c
p
k
n
i
o
i
d
f
e
But if we were somehow able
to associate entire paths with
a string, compressing links…
d
c
o
n
Note: All non-key-containing nodes with just
one child node have now vanished, and the
paths traversing them have been compressed
to a single link!
ar
id
Problems with tries
• “Non-key” nodes with just one (non-null) child are about as useful as
a Greek State Employee.
• Look at this example of a trie with two long keys, and two not so long
ones, but which do share a prefix:
root
root
s
a
r
c
p
k
n
i
o
i
d
f
e
But if we were somehow able
to associate entire paths with
a string, compressing links…
d
c
ar
id
o
n
Note: All non-key-containing nodes with just
one child node have now vanished, and the
paths traversing them have been compressed
to a single link!
So how can we associate
links with entire strings?
Patricia node structure
• We will add an integer index to every Patricia node.
• This index will give us the following information:
“In terms of all the keys that have already been inserted in the trie, in which character of the
input key should I be splitting into different paths?”
Patricia node structure
• We will add an integer index to every Patricia node.
• This index will give us the following information:
“In terms of all the keys that have already been inserted in the trie, in which character of the
input key should I be splitting into different paths?”
• Some trie-less examples:
• If our keys are just the words “tree” and “sun”, then a single root node with a
disambiguating index of 0 will split into two nodes.
• If we add the strings “treemap” and “treeset”, we need another node that
disambiguates at the 4th character (0-indexing employed)
(alphabet: lowercase english
characters)
In a trie…
Conceptual Image
Representation in memory
root
root
a b … … s
t … … y z
(node that contains “treemap”)
a b … … s
t … … y z
0
(node that contains “tree”)
(node that contains “sun”)
a b … … s
t … … y z
6
2
a b … m … s … … y z
3
(node that contains “treeset”)
a b … … s
t … … y z
6
(alphabet: lowercase english
characters)
In a trie…
Conceptual Image
Representation in memory
root
root
a b … … s
t … … y z
(node that contains “treemap”)
a b … … s
t … … y z
0
(node that contains “tree”)
(node that contains “sun”)
a b … … s
t … … y z
6
3
a b … m … s … … y z
4
(node that contains “treeset”)
a b … … s
t … … y z
Make sure you understand why the
indices are such!
6
(alphabet: lowercase english
characters)
In a trie…
Conceptual Image
Representation in memory
root
root
a b … … s
t … … y z
(node that contains “treemap”)
a b … … s
t … … y z
0
(node that contains “tree”)
(node that contains “sun”)
a b … … s
t … … y z
6
3
a b … m … s … … y z
(node that contains “treeset”)
a b … … s
t … … y z
Quiz: Is the root’s disambiguating index ever going to be greater than 0?
Yes
(example?)
No
(why?)
4
Make sure you understand why the
indices are such!
6
(alphabet: lowercase english
characters)
In a trie…
Conceptual Image
Representation in memory
root
root
a b … … s
Root node of a Patricia trie always
dummy!
See examples on Piazza.
t … … y z
(node that contains “treemap”)
a b … … s
t … … y z
0
(node that contains “tree”)
(node that contains “sun”)
a b … … s
t … … y z
6
3
a b … m … s … … y z
(node that contains “treeset”)
a b … … s
t … … y z
Quiz: Is the root’s disambiguating index ever going to be greater than 0?
Yes
(example?)
No
(why?)
4
Make sure you understand why the
indices are such!
6
Pop quiz
• Which keys are stored inside this Patricia Trie?
root
a b … … s
a b … … s
t … … y z
a b … … s
3
t … … y z
t … … y z
0
a b … m … s … … y z
8
a b … … s
4
t … … y z
7
Pop quiz
• Which keys are stored inside this Patricia Trie?
root
NO CLUE!
a b … … s
a b … … s
t … … y z
a b … … s
3
t … … y z
t … … y z
0
a b … m … s … … y z
8
a b … … s
4
t … … y z
7
Pop quiz
• Which keys are stored inside this Patricia Trie?
root
NO CLUE!
a b … … s
This might store
“sun” or “say”. Any
string of the form
s?? is fair game!
a b … … s
t … … y z
a b … … s
3
t … … y z
t … … y z
0
a b … m … s … … y z
8
a b … … s
4
t … … y z
This could be
storing “tree” or
“test”
7
Pop quiz
End-of-string flag
insufficient; we need
actual references to the
stored keys themselves!
• Which keys are stored inside this Patricia Trie?
root
NO CLUE!
a b … … s
This might store
“sun” or “say”. Any
string of the form
s?? is fair game!
a b … … s
t … … y z
a b … … s
3
t … … y z
t … … y z
0
a b … m … s … … y z
8
a b … … s
4
t … … y z
This could be
storing “tree” or
“test”
7
Pop quiz
End-of-string flag
insufficient; we need
actual references to the
stored keys themselves!
• Which keys are stored inside this Patricia Trie?
root
NO CLUE!
a b … … s
This might store
“sun” or “say”. Any
string of the form
s?? is fair game!
a b … … s
t … … y z
*3
t … … y z
x
*
0
a b … m … s … … y z
*
4
This could be
storing “tree” or
“test”
test
sun
a b … … s
t … … y z
*8
a b … … s
t … … y z
*
7
treeset
testable
Pop quiz
End-of-string flag
insufficient; we need
actual references to the
stored keys themselves!
• Which keys are stored inside this Patricia Trie?
root
NO CLUE!
a b … … s
This might store
“sun” or “say”. Any
string of the form
s?? is fair game!
a b … … s
t … … y z
*3
t … … y z
x
*
0
a b … m … s … … y z
*
4
This could be
storing “tree” or
“test”
test
sun
a b … … s
t … … y z
testable
*8
a b … … s
t … … y z
*
7
treeset
Make sure you are convinced that these strings
are all valid choices given our branchings!
Pop quiz
End-of-string flag
insufficient; we need
actual references to the
stored keys themselves!
• Which keys are stored inside this Patricia Trie?
root
NO CLUE!
a b … … s
This might store
“sun” or “say”. Any
string of the form
s?? is fair game!
a b … … s
t … … y z
*3
t … … y z
x
*
0
a b … m … s … … y z
*
4
New check to see if a
node has a key:
keyRef!=null
This could be
storing “tree” or
“test”
test
sun
a b … … s
t … … y z
testable
*8
a b … … s
t … … y z
*
7
treeset
Make sure you are convinced that these strings
are all valid choices given our branchings!
Searching
• Searching a key in a Patricia Trie should make use of the disambiguating index of
every node.
• This will speed up search considerably!
• Node’s ``disambiguating” (or ``splitting”) index named splitInd
• Suppose we are at a node 𝑛. We have some cases:
1. n.splitInd < key.length(). Subcases:
a)
b)
n.next[key.charAt(n.splitInd)]==null. Key not in tree!
n.next[key.charAt(n.splitInd)]!=null. Recurse into that node and keep
searching!
2. n.splitInd == key.length(). Subcases:
a)
b)
c)
n.keyRef==null. This means that the key is not in the tree!
n.keyRef!=null && n.keyRef.equals(key). This means that the key was found!
n.keyRef!=null && !n.keyRef.equals(key). This means that the key was not found!
3. n.splitInd > key.length(). This means that the key is not in the tree: it is a prefix
of another stored key!
Let’s code it up!
class ASCIIPatriciaTrie{
private class Node {
Node[] next = new Node[128];
String keyRef;
int splitInd;
}
private Node root;
public String search(String key){ // returns the key itself or null
Node keyContNode= search(root, key);
return (keyContNode != null) ? keyContNode.keyRef : null;
}
private Node search(Node n, String k){
/* You know what to do! Make sure you remember the different cases. */
}
}
Let’s code it up!
class ASCIIPatriciaTrie{
private class Node {
Node[] next = new Node[128];
String keyRef;
int splitInd;
}
private Node root;
public String search(String key){ // returns the key itself or null
Node keyContNode= search(root, key);
return (keyContNode != null) ? keyContNode.keyRef : null;
}
private Node search(Node n, String k){
if(n.splitInd < key.length()){
Node appropriateChild = n.next[key.charAt(n.splitInd)];
return (appropriateChild != null) ? search(appropriateChild, key) : null;
} else if (n.splitInd == key.length()){
return (n.stringRef != null) ? (n.stringRef.equals(k) ? n: null) : null;
} else {
return null;
}
}
}
This could work! Ternary operator
used just to conserve space; Jason
does not necessarily recommend its
widespread use.
Let’s code it up!
class ASCIIPatriciaTrie{
private class Node {
Node[] next = new Node[128];
String keyRef;
int splitInd;
}
private Node root;
public String search(String key){ // returns the key itself or null
Node keyContNode= search(root, key);
return (keyContNode != null) ? keyContNode.keyRef : null;
}
Main implementation difference with classic tries,
where we just increment our counter by 1.
private Node search(Node n, String k){
if(n.splitInd < key.length()){
Node appropriateChild = n.next[key.charAt(n.splitInd)];
return (appropriateChild != null) ? search(appropriateChild, key) : null;
} else if (n.splitInd == key.length()){
return (stringRef != null) ? this : null;
} else {
return null;
}
}
}
This could work! Ternary operator
used just to conserve space; Jason
does not necessarily recommend its
widespread use.
Let’s code it up!
class ASCIIPatriciaTrie{
private class Node {
Node[] next = new Node[128];
String keyRef;
IS THIS IMPLEMENTATION
TAIL RECURSIVE?
int splitInd;
Yes
}
No
private Node root;
public String search(String key){ // returns the key itself or null
Node keyContNode= search(root, key);
return (keyContNode != null) ? keyContNode.keyRef : null;
}
Main implementation difference with classic tries,
where we just increment our counter by 1.
private Node search(Node n, String k){
if(n.splitInd < key.length()){
Node appropriateChild = n.next[key.charAt(n.splitInd)];
return (appropriateChild != null) ? search(appropriateChild, key) : null;
} else if (n.splitInd == key.length()){
return (stringRef != null) ? this : null;
} else {
return null;
}
}
}
This could work! Ternary operator
used just to conserve space; Jason
does not necessarily recommend its
widespread use.
Let’s code it up!
class ASCIIPatriciaTrie{
private class Node {
Node[] next = new Node[128];
String keyRef;
int splitInd;
}
returns are last calls before
end of execution, in spite of
if/else if!
IS THIS IMPLEMENTATION
TAIL RECURSIVE?
Yes
No
private Node root;
public String search(String key){ // returns the key itself or null
Node keyContNode= search(root, key);
return (keyContNode != null) ? keyContNode.keyRef : null;
}
Main implementation difference with classic tries,
where we just increment our counter by 1.
private Node search(Node n, String k){
if(n.splitInd < key.length()){
Node appropriateChild = n.next[key.charAt(n.splitInd)];
return (appropriateChild != null) ? search(appropriateChild, key) : null;
} else if (n.splitInd == key.length()){
return (stringRef != null) ? this : null;
} else {
return null;
}
}
}
This could work! Ternary operator
used just to conserve space; Jason
does not necessarily recommend its
widespread use.
Let’s change our notation a bit
• Since your project involves Binary Patricia Tries, which use the minimal
alphabet {0,1}, we can actually simplify our node structure as follows:
private class BPTNode{
BPTNode left, right; // Instead of an array of size 2
x
int splitInd;
* 2
* *
String keyRef;
* 5
01010
*
1001 4
}
• We will use this notation to simplify our subsequent examples.
Insertion
• We have some cases to consider. Let’s begin with the easiest ones.
1. Currently examined node is null
• Allocate new node with key.
• Store its length as your splitInd
• Return this to parent (parent will have made recursive insertion call either
from left or right child)
Insertion
2. Currently examined node is not null
a) Length of key equal to splitInd
i. No key stored in node
• Then simply set the key stored in node to your input key.
ii. (Different) Key stored in node
• Then, I have to split node into 3 nodes:
Parent node, without a key and with splitInd equal to the length of the maximal
common prefix between the node’s key and the insertion key.
Two children nodes, one with the key of the old node and one with the key to be
inserted.
Which child will be left and which one will be right depends on the value of
key.charAt(node.splitInd)!
Insertion
2. Currently examined node is not null
b) Length of key smaller than splitInd
• This means that our key is the prefix of at least one existing key in the trie!
i.
ii.
Node has a (different) key stored.
• Split the node into two nodes:
Parent, which will store the new key, whose length is now splitInd.
Child (left or right) with previously existing key and its own splitInd.
The other child of the node will be null.
Node does not have a key stored. (most expensive case)
• Recurse into children to find maximal common prefix with key to be inserted. This is
necessary because we no longer have the string itself to easily compute the common prefix,
as in case 2.a.ii!
• Make a new node without a key, whose splitInd will be the length of this maximal
common prefix.
• Connect the new node with the node where the problem came up in the first place through
either left or right link, depending on value of key.charAt(node.splitInd)
• The other link will be the node with our key!
Insertion
2. Currently examined node is not null
b) Length of key smaller than splitInd
• This means that our key is the prefix of at least one existing key in the trie!
i.
ii.
Some people are
interested in optimizing
this by storing a bit in
their node class that will
give information about
which link to use for
connecting… While this
wastes a bit of space, it’s
A-ok with us!
Node has a (different) key stored.
• Split the node into two nodes:
Parent, which will store the new key, whose length is now splitInd.
Child (left or right) with previously existing key and its own splitInd.
The other child of the node will be null.
Node does not have a key stored. (most expensive case)
• R
• ecurse into children to find maximal common prefix with key to be inserted. This is necessary because we
no longer have the string itself to easily compute the common prefix, as in case 2.a.ii!
• Make a new node without a key, whose splitInd will be the length of this maximal common prefix.
• Connect the new node with the node where the problem came up in the first place through either left or
right link, depending on value of key.charAt(node.splitInd)
• The other link will be the node with our key!
Insertion
2. Currently examined node is not null
c) Length of key larger than splitInd
• Recurse appropriately, based on the value of key.charAt(node.splitInd)
Deletion
• Recall: In classic tries, we did not like blue nodes that only had one child.
• That’s why we came up with Patricia tries in the first place; we want to collapse
those to improve search efficiency.
• Only complex case in deletion: If you delete a key from a node that only
has a single child, that node is useless and you can erase it.
• This looks like it might contradict the “soft deletion” strategy that Jason
suggested in class, but it really doesn’t; there is still at least one node
with a key in the path towards the leaves!
• In fact, this strategy implements dynamic collapsing of freshly created blue
nodes, which, as we’ve stated, make our search inefficient!
Deletion
• Once again, we have some cases.
1. Current node is null.
• Then, this means that the key is not in the trie. We searched as much as we
could for it, but we fell off the trie.
Deletion
2. Current node is not null.
a) key.length() < splitInd
• Then this means that the key is not in the trie, since all the possible keys that follow this
path are guaranteed to be larger
Deletion
2. Current node is not null.
b) key.length() == splitInd
i.
Node.keyRef.key.compareTo(key) == 0
• Key found! Make keyRef null
• If you have only one child, you are like one of those pesky classic trie blue nodes with
only one child that we agreed we want to collapse with the Patricia trick; return your
only child!
• Otherwise, if you have 0 or two children, nothing you need to do after setting keyRef
to null.
ii. Node.keyRef.key.compareTo(key) != 0
•
•
Key not in the trie .
We know this because traversing the trie further down can only lead to lengthier keys, and to
get where we were, we followed pointers that examined chunks of our existing key to guide
us. All other paths are invalid for our key; we are sure it’s not in the trie.
Deletion
2. Current node is not null.
c) key.length() > splitInd
• Have to recurse appropriately depending on the value of
key.charAt(node.splitInd)!
© Copyright 2026 Paperzz