I translated the Wikipedia pseudo-code into Python code (little changes only were required), but I do not like the end result, as it looks like C with all those while loops and counters.
The fact is that I cannot use a for
loop as i
is needed to preserve state between loops.
I also added an inspect
method to actually see visually what is inside the trie.
The trie built from "trasaction", "transformation", "transmission", "trance", "trap", "trip"
is printed as:
e None
c None
n None
o None
i None
s None
s None
i None
m None
n None
o None
i None
t None
c None
a None
n None
o None
i None
t None
a None
m None
r None
o None
f None
s None
n None
p None
a None
p None
i None
r None
t None
As you can see, the common letters at the start are written only once, and going vertically you can read the possible endings.
Let's say I want to find trance:
e None < -- final
c None < -- semifinal
n None
o None
i None
s None
s None
i None
m None
n None
o None
i None
t None
c None
a None
n None
o None
i None
t None
a None
m None
r None
o None
f None
s None
n None < -- `n` is the needed letter
p None < -- `p` is not the needed letter, so we skip
a None < -- `a` is the needed letter
p None
i None < -- `i` is not the needed letter, so we skip
r None < -- We continue here (no choice but to)
t None < -- We start here (no choice but to)
The output is kinda reversed, but I would like to keep it so for simplicity of implementation.
import doctest
class Trie:
"""
Implements a Trie (also known as "digital tree", "radix tree" or "prefix tree").
Where common starting letters of many words are stored just once
"""
def __init__(self):
self.child = {}
def insert(self, s, value = 0):
"""
Adds a given string to the trie, modifying it **in place**
The optional value parameter is rather mysterious, my coe contains it just
because Wikipedia's pseudocode does too.
>>> t = Trie()
>>> t.insert("hello")
>>> t.insert("hi")
>>> Trie.inspect(t)
i None
o None
l None
l None
e None
h None
As you can see, the `h` was written only once, even if
both words start with it.
"""
node = self
i = 0
n = len(s)
while i < n:
try:
_ = node.child[s[i]]
node = node.child[s[i]]
i = i + 1
except KeyError:
break
# (* append new nodes, if necessary *)
while i < n:
node.child[s[i]] = Trie()
node = node.child[s[i]]
i = i + 1
node.value = value
@staticmethod
def find(trie, word):
"""
Returns True if the given word is the Trie, False if it is not.
>>> t = Trie()
>>> t.insert("example")
>>> Trie.find(t, "example")
True
>>> Trie.find(t, "exemplum")
False
"""
if not word:
return True
for char in word:
if char == word[-1]:
return True
if char in trie.child:
trie = trie.child[char]
else:
return False
@staticmethod
def inspect(trie, deepness = 0):
"""
Shows a nicely formatted and indented Trie.
Cannot be tested as equivalent representations are randomly chosen from.
"""
for i in trie.child:
print ("{}{} {}".format(
" " * deepness, i, Trie.inspect(trie.child[i], deepness + 1)))
if __name__ == "__main__":
doctest.testmod()
(Minor note: I noticed that inspect
outputs equivalent but visually different tries on different runs, if a doctest where it is used fails, do not worry.)