In [1]:
%%HTML
<style>
.container { width:100% } 
</style>


Tries

A Trie is a data structure that implements the abstract data type of a dictionary in the case that the keys are strings. The set $\mathbb{T}$ of all tries is defined inductively using the constructor $$ \texttt{Node}: \texttt{Value} \times \texttt{List}(\Sigma) \times\texttt{List}(\mathbb{T}) \rightarrow \mathbb{T}. $$ Here $\Sigma$ is the alphabet, i.e. the set of characters. The inductive definition of the set $\mathbb{T}$ has only a single clause: If

  • $v \in \texttt{Value} \cup \{\Omega\}$,
  • $C\!s = [c_1, \cdots, c_n] \in \texttt{List}(\Sigma)$ is a list of different characters of length $n$ and
  • $T\!s = [t_1, \cdots, t_n] \in \texttt{List}(\mathbb{T})$ is a list of tries of the same length $n$,

then we have $$ \texttt{Node}(v, C\!s, T\!s) \in \mathbb{T}. $$

An object $t$ of the class Trie represents the trie $$ \texttt{Node}(t.\texttt{mValue}, t.\texttt{mChars}, t.\texttt{mTries}). $$ The constructor below creates an empty trie.


In [2]:
class Trie:
    def __init__(self):
        self.mValue  = None
        self.mChars  = []
        self.mTries  = []
        self.mParent = '' # only used by graphviz

In order to specify the meaning of the class Trie we define the function find, which can be used to retrieve the values stored in a trie. This function has the signature $$ \texttt{find}: \mathbb{T} \times \Sigma^* \rightarrow \texttt{Value} \cup \{ \Omega\}. $$ For a trie $t$ and a string $s$, the expression $t.\texttt{find}(s)$ returns the value that is associated with the key $s$ in the trie $t$. The expression $\texttt{Node}(v,C\!s,T\!s).\texttt{find}(s)$ is defined by induction on the length of the string $s$:

  • $\texttt{Node}(v, C\!s, T\!s).\texttt{find}(\varepsilon) = v$.

    The value associated with the empty string $\varepsilon$ is stored at the root of the trie.

  • $c = c_i \rightarrow \texttt{Node}(v, [c_1, \cdots, c_n], [t_1, \cdots, t_n]).\texttt{find}(cr) = t_i.\texttt{find}(r) $

    The trie $\texttt{Node}(v, [c_1, \cdots, c_n], [t_1, \cdots, t_n])$ associates a value with the key $cr$ if the list $[c_1, \cdots, c_n]$ has a position $i$ such that $c$ equals $c_i$ and, furthermore, the trie $t_i$ associates a value with the key $r$.

  • $ c \not\in C\!s \rightarrow \texttt{Node}(v, C\!s, T\!s).\texttt{find}(cr) = \Omega $

    If $c$ does not occur in the list $C\!s$, then the trie $\texttt{Node}(v, C\!s, T\!s)$ does not store a value for the key $cr$.


In [4]:
def find(self, s):
    if s == '':
        return self.mValue
    c, r = s[0], s[1:]
    for i, ci in enumerate(self.mChars):
        if c == ci:
            return self.mTries[i].find(r)
    return None # not necessary

Trie.find = find
del find

In [3]:
list(enumerate(['a', 'b', 'c']))


Out[3]:
[(0, 'a'), (1, 'b'), (2, 'c')]

The signature of the method $\texttt{insert}$ is given as follows: $$ \texttt{insert}: \mathbb{T} \times \Sigma^* \times \texttt{Value} \rightarrow \mathbb{T}. $$ The result of evaluating $$ \texttt{Node}(v_1, [c_1, \cdots, c_n], [t_1, \cdots, t_n]).\texttt{insert}(w, v_2) $$ for a string $w\in \Sigma^*$ and a value $v_2 \in \texttt{Value}$ is defined by induction on the length of $w$:

  • $\texttt{Node}(v_1,L,T).\texttt{insert}(\varepsilon, v_2) = \texttt{Node}(v_2,L,T)$.

    If a new value $v_2$ is associated with the empty string $\varepsilon$, then the old value $v_1$, which had been stored at the root before, is overwritten.

  • $\texttt{Node}\bigl(v_1,[c_1,\cdots,c_i,\cdots,c_n], [t_1,\cdots,t_i,\cdots,t_n]\bigr).\texttt{insert}(c_ir,v_2) = \texttt{Node}\bigl(v_1,[c_1,\cdots,c_i,\cdots,c_n], [t_1,\cdots,t_i.\texttt{insert}(r,v_2),\cdots,t_n]\bigr)$.

    In order to associate a value $v_2$ with the string $c_ir$ in the trie $$ \texttt{Node}\bigl(v_1,[c_1,\cdots,c_i,\cdots,c_n], [t_1,\cdots,t_i,\cdots,t_n]\bigr) $$ we have to recursively associate the value $v_2$ with the string $r$ in the trie $t_i$.

  • $c \not\in\{c_1,\cdots,c_n\} \;\rightarrow\;\texttt{Node}\bigl(v_1,[c_1,\cdots,c_n], [t_1,\cdots,t_n]\bigr).\texttt{insert}(cr,v_2) = \texttt{Node}\bigl(v_1,[c_1,\cdots,c_n,c], [t_1,\cdots,t_n,\texttt{Node}(\Omega,[],[]).\texttt{insert}(r,v_2)]\bigr)$.

    If we want to associate a value $v$ with the key $cr$ in the trie $\texttt{Node}\bigl(v_1,[c_1,\cdots,c_n], [t_1,\cdots,t_n]\bigr)$ then, if the character $c$ does not occur in the list $[c_1,\cdots,c_n]$, we first have to create a new empty trie. This trie has the form $$ \texttt{Node}(\Omega, [], []). $$ Next, we associate the value $v_2$ with the key $r$ in this empty trie. Finally, we append the character $c$ to the end of the list $[c_1,\cdots,c_n]$ and append the trie $$ \texttt{Node}(\Omega, [], []).\texttt{insert}(r,v_2) $$ to the end of the list $[t_1,\cdots,t_n]$.


In [5]:
def insert(self, s, v):
    if s == '':
        self.mValue = v
        return
    c, r = s[0], s[1:]
    for i, ci in enumerate(self.mChars):
        if c == ci:
            self.mTries[i].insert(r, v)
            return
    t = Trie()
    t.insert(r, v)
    t.mParent = c # necessary for visualization
    self.mChars.append(c)
    self.mTries.append(t)
    
Trie.insert = insert
del insert

In order to implement deletion in tries, we need the auxiliary function isEmpty. Its signature is given as $$ \texttt{isEmpty}: \mathbb{T} \rightarrow \mathbb{B}. $$ For a trie $t$, we have $t.\texttt{isEmpty}() = \texttt{True}$ if and only if the trie $t$ does not store any key. The following formula specifies the function $\texttt{isEmpty}$: $$ \texttt{Node}(v, L, T).\texttt{isEmpty}() = \mathtt{True} \Leftrightarrow v = \Omega \wedge L = []. $$


In [6]:
def isEmpty(self):
    return self.mValue == None and self.mChars == []

Trie.isEmpty = isEmpty
del isEmpty

For a trie $t \in \mathbb{T}$ and a string $w \in \Sigma^*$ the value $t.\texttt{delete}(w)$ is defined by induction on the length of $w$.

  • $\texttt{Node}(v,C,T).\texttt{delete}(\varepsilon) = \texttt{Node}(\Omega,C,T)$

    The value that is associated with the empty string $\varepsilon$ is stored at the root of the trie where is can be deleted without further ado.

  • $\begin{array}[t]{ll} t_i.\texttt{delete}(r).\texttt{isEmpty}() & \rightarrow \\ \texttt{Node}(v, [c_1,\cdots,c_i,\cdots,c_n],[t_1,\cdots,t_i,\cdots,t_n]).\texttt{delete}(c_ir) & = \\ \qquad \texttt{Node}(v, [c_1,\cdots,c_{i-1},c_{i+1},\cdots,c_n],[t_1,\cdots,t_{i-1},t_{i+1},\cdots,t_n]). \end{array} $

    If the key that is to be deleted starts with the character $c_i$ and if deletion of the key $r$ in the $i$th trie $t_i$ yields an empty trie, then both the $i$th character $c_i$ and the $i$th trie $t_i$ are deleted.

  • $\begin{array}[t]{ll} \neg t_i.\texttt{delete}(r).\texttt{isEmpty}() & \rightarrow \\ \texttt{Node}(v, [c_1,\cdots,c_i,\cdots,c_n],[t_1,\cdots,t_i,\cdots,t_n]).\texttt{delete}(c_ir) & = \\ \qquad \texttt{Node}(v, [c_1,\cdots,c_i,\cdots,c_n],[t_1,\cdots,t_i.\texttt{delete}(r),\cdots,t_n]). \end{array} $

    If the key that is to be deleted starts with the character $c_i$ and if deletion of the key $r$ in the $i$th trie $t_i$ yields a non-empty trie, then the key $r$ has to be deleted recursively in the trie $t_i$.

  • $c \notin C \rightarrow \texttt{Node}(v, C, T).\texttt{delete}(cr) = \texttt{Node}(v, C, T)$.

    If the key that is to be deleted starts with the character $c$ and $c$ does not occur in the list of characters $C$, then the trie does not contain the key $cr$ and therefore there is nothing to do: The trie is left unchanged.


In [7]:
def delete(self, s):
    if s == '':
        self.mValue = None
        return
    c, r = s[0], s[1:]
    for i, ci in enumerate(self.mChars):
        if c == ci:
            self.mTries[i].delete(r)
            if self.mTries[i].isEmpty():
                self.mChars.pop(i)
                self.mTries.pop(i)
            return
        
Trie.delete = delete
del delete

String Completion

In order to implement string completion we define the function allKeys, which has the following signature: $$\texttt{allKeys}: \mathbb{T} \times \Sigma^* \rightarrow \texttt{Set}(\Sigma^*)$$ Given a trie $T$ and a string $p$, the function $T.\texttt{allKeys}(p)$ computes the set of all strings that are stored as keys in the trie $T$. Furthermore, the string $p$ is added as a prefix to all these string. Therefore we specify the semantics of the function $\texttt{allKeys}$ as follows: $$ T.\texttt{allKeys}(p) = \{ p+w \mid w \in T \}. $$ Here, $p+w$ denotes the concatenation of the strings $p$ and $w$ and the expression $w \in T$ is true iff $T.\texttt{find}(w) \not= \Omega$.

Given a trie $T$, the value $T.\texttt{allKeys}(p)$ is computed by induction on $T$. There are two cases:

  • $\texttt{Node}(\Omega, [c_1, \cdots, c_n], [t_1,\cdots,t_n]).\texttt{allKeys}(p) = \bigcup\limits_{i=1}^n t_i.\texttt{allKeys}(p+c_i) $,
  • $v \not= \Omega \rightarrow \texttt{Node}(v, [c_1, \cdots, c_n], [t_1,\cdots,t_n]).\texttt{allKeys}(p) = \{p\} \cup \bigcup\limits_{i=1}^n t_i.\texttt{allKeys}(p+c_i) $.

In [8]:
def allKeys(self, prefix=''):
    Result = set()
    if self.mValue != None:
        Result.add(prefix)
    for i, ci in enumerate(self.mChars):
        Result |= self.mTries[i].allKeys(prefix + ci)
    return Result

Trie.allKeys = allKeys
del allKeys

Next, we specify the auxiliary function _findSuffix that has the following signature: $$ \texttt{_findSuffix}: \mathbf{T} \times \Sigma^* \times \Sigma^* \rightarrow \texttt{Set}(\Sigma^*) $$ Given a trie $T$, a string $s$, and a string $p$, it returns the set of all strings that are used as keys in $T$ and, furthermore, have the prefix $s$. Additionally, it replaces the prefix $s$ with the string $p$. Therefore we have $$ T.\texttt{_findSuffix}(s, p) = \bigl\{ p+r \mid s + r \in T \bigr\}. $$ The second argument $p$ is needed in order to make the inductive definition of the function _findSuffix work out. This definition is given next:

  • $T.\texttt{_findSuffix}(\varepsilon, p) = T.\texttt{allKeys}(p)$,
  • $\texttt{Node}\bigl(v, [c_1, \cdots, c_n], [t_1,\cdots,t_n]\bigr).\texttt{_findSuffix}(c_ir, p) = t_i.\texttt{_findSuffix}(r, p) $,
  • $c \not\in C\!s \rightarrow \texttt{Node}(v, C\!s, T\!s).\texttt{_findSuffix}(cr, p) = \{\}$.

In [9]:
def _findSuffix(self, s, p):
    if s == '':
        return self.allKeys(p)
    c, r = s[0], s[1:]
    for i, ci in enumerate(self.mChars):
        if c == ci:
            return self.mTries[i]._findSuffix(r, p)
    return set()

Trie._findSuffix = _findSuffix
del _findSuffix

Finally, we specify the function $$\texttt{findSuffix}:\mathbb{T} \times \Sigma^* \rightarrow \texttt{Set}(\Sigma^*)$$ so that given a trie $T$ and a prefix $p$, the expression $T.\texttt{findSuffix}(p)$ finds all strings $s \in T$ that have the prefix $p$, i.e. that can be written in the form $s=p+r$: $$ T.\texttt{findSuffix}(p) = \bigl\{ p+r \mid p+r \in T \bigr\}. $$ Having defined the auxiliary function \texttt{_findSuffix}, the function \texttt{findSuffix} can be implemented as follows: $$ T.\texttt{findSuffix}(s) = T.\texttt{_findSuffix}(s, s). $$


In [10]:
def findSuffix(self, s):
    return self._findSuffix(s, s)

Trie.findSuffix = findSuffix
del findSuffix

Graphical Representation via GraphViz


In [11]:
import graphviz as gv

In [12]:
def toDot(self):
    Trie.sNodeCount = 0 # this is a static variable of the class Trie
    dot = gv.Digraph(node_attr={'shape': 'record', 'style': 'rounded'})
    NodeDict = {}
    self._assignIDs(NodeDict)
    for id, t in NodeDict.items():
        if t.mValue != None:
            dot.node(id, label='{' + t.mParent + '|' + str(t.mValue) + '}')
        else:
            dot.node(id, label=t.mParent)
    for id, t in NodeDict.items():
        for x in t.mTries:
            dot.edge(id, x.mID)
    return dot

Trie.toDot = toDot
del toDot

In [13]:
def _assignIDs(self, NodeDict):
    Trie.sNodeCount += 1
    self.mID = str(Trie.sNodeCount)
    NodeDict[self.mID] = self
    for t in self.mTries:
        t._assignIDs(NodeDict) 
    
Trie._assignIDs = _assignIDs
del _assignIDs

Testing


In [14]:
t = Trie()
t.toDot()


Out[14]:
%3 1

In [15]:
t.insert("Hans", 1)
t.toDot()


Out[15]:
%3 1 2 H 1->2 3 a 2->3 4 n 3->4 5 s 1 4->5

In [16]:
t.insert("Hanna", 2)
t.toDot()


Out[16]:
%3 1 2 H 1->2 3 a 2->3 4 n 3->4 5 s 1 4->5 6 n 4->6 7 a 2 6->7

In [17]:
t.insert("Hilde", 3)
t.toDot()


Out[17]:
%3 1 2 H 1->2 3 a 2->3 8 i 2->8 4 n 3->4 5 s 1 4->5 6 n 4->6 7 a 2 6->7 9 l 8->9 10 d 9->10 11 e 3 10->11

In [18]:
t.insert("Hugo", 4)
t.toDot()


Out[18]:
%3 1 2 H 1->2 3 a 2->3 8 i 2->8 12 u 2->12 4 n 3->4 5 s 1 4->5 6 n 4->6 7 a 2 6->7 9 l 8->9 10 d 9->10 11 e 3 10->11 13 g 12->13 14 o 4 13->14

In [19]:
t.findSuffix('Hi')


Out[19]:
{'Hilde'}

In [20]:
t.insert("Harro", 5)
t.toDot()


Out[20]:
%3 1 2 H 1->2 3 a 2->3 11 i 2->11 15 u 2->15 4 n 3->4 8 r 3->8 5 s 1 4->5 6 n 4->6 7 a 2 6->7 9 r 8->9 10 o 5 9->10 12 l 11->12 13 d 12->13 14 e 3 13->14 16 g 15->16 17 o 4 16->17

In [21]:
t.insert("Heike", 6)
t.toDot()


Out[21]:
%3 1 2 H 1->2 3 a 2->3 11 i 2->11 15 u 2->15 18 e 2->18 4 n 3->4 8 r 3->8 5 s 1 4->5 6 n 4->6 7 a 2 6->7 9 r 8->9 10 o 5 9->10 12 l 11->12 13 d 12->13 14 e 3 13->14 16 g 15->16 17 o 4 16->17 19 i 18->19 20 k 19->20 21 e 6 20->21

In [22]:
t.insert("Heiko", 7)
t.toDot()


Out[22]:
%3 1 2 H 1->2 3 a 2->3 11 i 2->11 15 u 2->15 18 e 2->18 4 n 3->4 8 r 3->8 5 s 1 4->5 6 n 4->6 7 a 2 6->7 9 r 8->9 10 o 5 9->10 12 l 11->12 13 d 12->13 14 e 3 13->14 16 g 15->16 17 o 4 16->17 19 i 18->19 20 k 19->20 21 e 6 20->21 22 o 7 20->22

In [24]:
t.find('Hilde')


Out[24]:
3

In [25]:
t.insert("Harald", 8)
t.insert("Hasso", 9)
t.insert("Haley", 1)
t.insert("Hanny", 1)
t.insert("Happy", 1)
t.insert("Harlene", 1)
t.insert("Harley", 1)
t.insert("Harmony", 1)
t.insert("Harriet", 1)
t.insert("Harrietta", 1)
t.insert("Hazel", 1)
t.insert("Heather", 1)
t.insert("Hedwig", 1)
t.insert("Hedy", 1)
t.insert("Heidi", 1)
t.insert("Helaina", 1)
t.insert("Helaine", 1)
t.insert("Helen", 1)
t.insert("Helena", 1)
t.insert("Helene", 1)
t.insert("Helga", 1)
t.insert("Hendrika", 1)
t.insert("Henrietta", 1)
t.insert("Henriette", 1)
t.insert("Hermione", 1)
t.insert("Herta", 1)
t.insert("Hilary", 1)
t.insert("Hilda", 1)
t.toDot()


Out[25]:
%3 1 2 H 1->2 3 a 2->3 41 i 2->41 49 u 2->49 52 e 2->52 4 n 3->4 9 r 3->9 29 s 3->29 32 l 3->32 35 p 3->35 38 z 3->38 5 s 1 4->5 6 n 4->6 7 a 2 6->7 8 y 1 6->8 10 r 9->10 17 a 9->17 20 l 9->20 25 m 9->25 11 o 5 10->11 12 i 10->12 13 e 12->13 14 t 1 13->14 15 t 14->15 16 a 1 15->16 18 l 17->18 19 d 8 18->19 21 e 20->21 22 n 21->22 24 y 1 21->24 23 e 1 22->23 26 o 25->26 27 n 26->27 28 y 1 27->28 30 s 29->30 31 o 9 30->31 33 e 32->33 34 y 1 33->34 36 p 35->36 37 y 1 36->37 39 e 38->39 40 l 1 39->40 42 l 41->42 43 d 42->43 46 a 42->46 44 e 3 43->44 45 a 1 43->45 47 r 46->47 48 y 1 47->48 50 g 49->50 51 o 4 50->51 53 i 52->53 59 a 52->59 64 d 52->64 69 l 52->69 81 n 52->81 94 r 52->94 54 k 53->54 57 d 53->57 55 e 6 54->55 56 o 7 54->56 58 i 1 57->58 60 t 59->60 61 h 60->61 62 e 61->62 63 r 1 62->63 65 w 64->65 68 y 1 64->68 66 i 65->66 67 g 1 66->67 70 a 69->70 75 e 69->75 79 g 69->79 71 i 70->71 72 n 71->72 73 a 1 72->73 74 e 1 72->74 76 n 1 75->76 77 a 1 76->77 78 e 1 76->78 80 a 1 79->80 82 d 81->82 87 r 81->87 83 r 82->83 84 i 83->84 85 k 84->85 86 a 1 85->86 88 i 87->88 89 e 88->89 90 t 89->90 91 t 90->91 92 a 1 91->92 93 e 1 91->93 95 m 94->95 100 t 94->100 96 i 95->96 97 o 96->97 98 n 97->98 99 e 1 98->99 101 a 1 100->101

In [26]:
t.findSuffix('Hea')


Out[26]:
{'Heather'}

In [27]:
t.findSuffix('Hel')


Out[27]:
{'Helaina', 'Helaine', 'Helen', 'Helena', 'Helene', 'Helga'}

In [28]:
t.delete('Hanny')
t.toDot()


Out[28]:
%3 1 2 H 1->2 3 a 2->3 40 i 2->40 48 u 2->48 51 e 2->51 4 n 3->4 8 r 3->8 28 s 3->28 31 l 3->31 34 p 3->34 37 z 3->37 5 s 1 4->5 6 n 4->6 7 a 2 6->7 9 r 8->9 16 a 8->16 19 l 8->19 24 m 8->24 10 o 5 9->10 11 i 9->11 12 e 11->12 13 t 1 12->13 14 t 13->14 15 a 1 14->15 17 l 16->17 18 d 8 17->18 20 e 19->20 21 n 20->21 23 y 1 20->23 22 e 1 21->22 25 o 24->25 26 n 25->26 27 y 1 26->27 29 s 28->29 30 o 9 29->30 32 e 31->32 33 y 1 32->33 35 p 34->35 36 y 1 35->36 38 e 37->38 39 l 1 38->39 41 l 40->41 42 d 41->42 45 a 41->45 43 e 3 42->43 44 a 1 42->44 46 r 45->46 47 y 1 46->47 49 g 48->49 50 o 4 49->50 52 i 51->52 58 a 51->58 63 d 51->63 68 l 51->68 80 n 51->80 93 r 51->93 53 k 52->53 56 d 52->56 54 e 6 53->54 55 o 7 53->55 57 i 1 56->57 59 t 58->59 60 h 59->60 61 e 60->61 62 r 1 61->62 64 w 63->64 67 y 1 63->67 65 i 64->65 66 g 1 65->66 69 a 68->69 74 e 68->74 78 g 68->78 70 i 69->70 71 n 70->71 72 a 1 71->72 73 e 1 71->73 75 n 1 74->75 76 a 1 75->76 77 e 1 75->77 79 a 1 78->79 81 d 80->81 86 r 80->86 82 r 81->82 83 i 82->83 84 k 83->84 85 a 1 84->85 87 i 86->87 88 e 87->88 89 t 88->89 90 t 89->90 91 a 1 90->91 92 e 1 90->92 94 m 93->94 99 t 93->99 95 i 94->95 96 o 95->96 97 n 96->97 98 e 1 97->98 100 a 1 99->100

In [29]:
t.insert('Auto', 7)
t.toDot()


Out[29]:
%3 1 2 H 1->2 101 A 1->101 3 a 2->3 40 i 2->40 48 u 2->48 51 e 2->51 4 n 3->4 8 r 3->8 28 s 3->28 31 l 3->31 34 p 3->34 37 z 3->37 5 s 1 4->5 6 n 4->6 7 a 2 6->7 9 r 8->9 16 a 8->16 19 l 8->19 24 m 8->24 10 o 5 9->10 11 i 9->11 12 e 11->12 13 t 1 12->13 14 t 13->14 15 a 1 14->15 17 l 16->17 18 d 8 17->18 20 e 19->20 21 n 20->21 23 y 1 20->23 22 e 1 21->22 25 o 24->25 26 n 25->26 27 y 1 26->27 29 s 28->29 30 o 9 29->30 32 e 31->32 33 y 1 32->33 35 p 34->35 36 y 1 35->36 38 e 37->38 39 l 1 38->39 41 l 40->41 42 d 41->42 45 a 41->45 43 e 3 42->43 44 a 1 42->44 46 r 45->46 47 y 1 46->47 49 g 48->49 50 o 4 49->50 52 i 51->52 58 a 51->58 63 d 51->63 68 l 51->68 80 n 51->80 93 r 51->93 53 k 52->53 56 d 52->56 54 e 6 53->54 55 o 7 53->55 57 i 1 56->57 59 t 58->59 60 h 59->60 61 e 60->61 62 r 1 61->62 64 w 63->64 67 y 1 63->67 65 i 64->65 66 g 1 65->66 69 a 68->69 74 e 68->74 78 g 68->78 70 i 69->70 71 n 70->71 72 a 1 71->72 73 e 1 71->73 75 n 1 74->75 76 a 1 75->76 77 e 1 75->77 79 a 1 78->79 81 d 80->81 86 r 80->86 82 r 81->82 83 i 82->83 84 k 83->84 85 a 1 84->85 87 i 86->87 88 e 87->88 89 t 88->89 90 t 89->90 91 a 1 90->91 92 e 1 90->92 94 m 93->94 99 t 93->99 95 i 94->95 96 o 95->96 97 n 96->97 98 e 1 97->98 100 a 1 99->100 102 u 101->102 103 t 102->103 104 o 7 103->104

In [30]:
t.allKeys()


Out[30]:
{'Auto',
 'Haley',
 'Hanna',
 'Hans',
 'Happy',
 'Harald',
 'Harlene',
 'Harley',
 'Harmony',
 'Harriet',
 'Harrietta',
 'Harro',
 'Hasso',
 'Hazel',
 'Heather',
 'Hedwig',
 'Hedy',
 'Heidi',
 'Heike',
 'Heiko',
 'Helaina',
 'Helaine',
 'Helen',
 'Helena',
 'Helene',
 'Helga',
 'Hendrika',
 'Henrietta',
 'Henriette',
 'Hermione',
 'Herta',
 'Hilary',
 'Hilda',
 'Hilde',
 'Hugo'}

In [32]:
t.delete("Hans")
t.delete("Hanna")
t.delete("Hilde")
t.delete("Hugo")
t.delete("Harro")
t.delete("Heike")
t.delete("Heiko")
t.delete("Harald")
t.delete("Hasso")
t.delete("Haley")
t.delete("Hanny")
t.delete("Happy")
t.delete("Harlene")
t.delete("Harley")
t.delete("Harmony")
t.delete("Harriet")
t.delete("Harrietta")
t.delete("Hazel")
t.delete("Heather")
t.delete("Hedwig")
t.delete("Hedy")
t.delete("Heidi")
t.delete("Helaina")
t.delete("Helaine")
t.delete("Helen")
t.delete("Helena")
t.delete("Helene")
t.delete("Helga")
t.delete("Hendrika")
t.delete("Henrietta")
t.delete("Henriette")
t.delete("Hermione")
t.delete("Herta")
t.delete("Hilary")
t.delete("Hilda")
t.toDot()


Out[32]:
%3 1 2 A 1->2 3 u 2->3 4 t 3->4 5 o 7 4->5

Let us compute the prime numbers less than $100$. Note that we have to convert numbers to strings in order to be able to use tries.


In [33]:
S = Trie()
for i in range(2, 101):
    S.insert(str(i), i)
for i in range(2, 51):
    for j in range(i, 100 // i + 1):
        S.delete(str(i * j))
S.toDot()


Out[33]:
%3 1 2 2 2 1->2 5 3 3 1->5 8 4 1->8 12 5 5 1->12 15 6 1->15 18 7 7 1->18 22 8 1->22 25 9 1->25 27 1 1->27 3 3 23 2->3 4 9 29 2->4 6 1 31 5->6 7 7 37 5->7 9 1 41 8->9 10 3 43 8->10 11 7 47 8->11 13 3 53 12->13 14 9 59 12->14 16 1 61 15->16 17 7 67 15->17 19 1 71 18->19 20 3 73 18->20 21 9 79 18->21 23 3 83 22->23 24 9 89 22->24 26 7 97 25->26 28 1 11 27->28 29 3 13 27->29 30 7 17 27->30 31 9 19 27->31

Let us compute all key stored in this trie, as these are the prime numbers.


In [34]:
print(sorted([int(p) for p in S.allKeys()]))


[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

In [ ]: