After experimenting with a sorted trie implementation in C, I felt that I understood tries pretty well, but was having trouble explaining how they work. Since the C code was based on existing code, I wanted to try it again in another language, from scratch.
Here's what I came up with. I tried to make it as minimal as possible. These tries aren't sorted, and nodes only reference a child
and next
node; parent
and prev
aren't used. I did give the "leaves" of the trie a name
property, which is the full key. This isn't currently used, but it's useful for debugging, and it would be useful if a trie iterator were added (like Trie_dump
from the question linked above).
I'm interested in feedback on the code itself this time, but not so much on things like white space, braces, semicolons, etc. I'm comfortable with the formatting the way it is. I'd be interested in an alternative to that labeled loop, though, and all other feedback is welcome.
Trie structure
Each node in the trie can have a key
, a value
, a next
node, and a child
node. In this diagram, each node is represented by a circle. The "root" node is at the left, the green "leaf" nodes are at the right, and the "branch" nodes are in the middle.
Each key
is a single character (circled letters below). The "branches" have keys, while each "leaf" holds a value.
In the diagram, nodes appear to have multiple children. For example, the first "r" node has "u" and "o" children. Internally, nodes just have a child
property; the first child. The next child is available in the first child's next
property, and so on. It may help to think of child
as "first child" and next
as "next sibling."
trie.js
function Trie(parent, prev, key, value) {
if (key !== void 0)
this.key = key; // single-character key
if (value !== void 0)
this.value = value; // user-defined value
if (prev)
prev.next = this; // next sibling node
else if (parent)
parent.child = this; // first child node
}
// put a key/value pair in the trie
Trie.prototype.put = function(name, value) {
var i = 0, t = this, len = name.length, prev, parent;
down: while (t.child) {
parent = t;
t = t.child;
// if first child didn't match, get next sibling
while (t.key != name[i]) {
if (!t.next) {
prev = t;
t = parent;
break down;
}
t = t.next;
}
// key already exists, update the value
if (++i > len) {
t.value = value;
return;
}
}
// found any existing parts of the key, add the rest
t = new this.constructor(t, prev, name[i]);
while (++i <= len)
t = new this.constructor(t, null, name[i]);
t.name = name;
t.value = value;
};
// get a value from the trie at the given key
Trie.prototype.get = function(name) {
var i = 0, t = this.child, len = name.length;
while (t) {
if (t.key == name[i]) {
if (i == len)
return t.value;
t = t.child;
++i;
} else {
t = t.next;
}
}
};
Demos
Here are links to a few demos. The simple demo puts some test data in a trie, gets it back out, and dumps it to the console. The fancy demo puts the same test data in a trie and draws a diagram like the one above.
Simple demo: http://jsfiddle.net/4Yttq
Fancy demo: http://jsfiddle.net/4Yttq/1/
while (++i <= len)
isn't something comprehensible. Not using braces forif
, etc. The code is pretty, but it feels like magic on first sight. \$\endgroup\$break down;
:) \$\endgroup\$