dsa.trie API documentation

# dsa.trie

Module containing trie class.

View Source

      1""" Module containing trie class. """
      2
      3class TrieNode:
      4    """
      5    A trie node implementation.
      6    """
      7    def __init__(self):
      8        #: a dictionary of key (character) ->value (references)
      9        self.children = {}
     10
     11class Trie:
     12    """
     13    A trie implementation.
     14    """
     15    #: end marker
     16    end_marker = "*"
     17
     18    def __init__(self):
     19        """
     20        Initialize a trie.
     21        """
     22        #: root of the trie
     23        self.root = TrieNode()
     24
     25    def insert(self, word: str):
     26        """
     27        Insert a word into a trie.
     28
     29        Args:
     30            word (str): The word to insert.
     31
     32        Returns:
     33            None
     34        """
     35        current = self.root
     36        for c in word:
     37            if c not in current.children:
     38                current.children[c] = TrieNode()
     39
     40            current = current.children[c]
     41        current.children[Trie.end_marker] = None
     42
     43    def search(self, word: str) -> bool:
     44        """
     45        Search for a word in a trie.
     46
     47        Args:
     48            word (str): The word to search for.
     49
     50        Returns:
     51            Boolean on whether the complete word is found.
     52        """
     53        if len(word) == 0:
     54            return False
     55
     56        current = self.root
     57        for c in word + Trie.end_marker:
     58            if c not in current.children:
     59                return False
     60            current = current.children[c]
     61        return True
     62
     63    def search_node(self, substr: str) -> TrieNode:
     64        """
     65        Search for a substring in a trie.
     66
     67        Args:
     68            substr (str): A substring to search for.
     69
     70        Returns:
     71            TriedNode where the substring begins
     72            None if the substring is not found
     73        """
     74        if len(substr) == 0:
     75            return None
     76
     77        current = self.root
     78        for c in substr:
     79            if c not in current.children:
     80                return None
     81            current = current.children[c]
     82        return current
     83
     84    def delete(self, word: str, i: int=0, current: TrieNode=None) -> bool:
     85        """
     86        Delete a word from the trie.
     87
     88        Args:
     89            word (str): The word to delete.
     90            i (int): The index of character.
     91            current (TrieNode): The current node.
     92
     93        Returns:
     94            Boolean indicating if child node can be deleted.
     95
     96        Raises:
     97            ValueError: If the word is not found.
     98        """
     99        if i == len(word):
    100            return True
    101
    102        # set up the beginning of delete
    103        if current is None:
    104            current = self.root
    105            word = word + Trie.end_marker
    106
    107        char = word[i]
    108
    109        # character is not found, so word is invalid and return False
    110        if char not in current.children:
    111            return False
    112
    113        next_node = current.children[char]
    114        should_delete_ref = self.delete(word, i + 1, next_node)
    115
    116        # delete the child reference
    117        if should_delete_ref:
    118            del current.children[char]
    119
    120            # return True if there are no more children
    121            return len(current.children) == 0
    122        return False
    123
    124    def delete_preorder(self, word: str, i: int=0, current: TrieNode=None):
    125        """
    126        Delete a word using preorder (Do not use! For demonstration purposes only).
    127
    128        Args:
    129            word (str): The word to delete.
    130            i (int): The index of character.
    131            current (TrieNode): The current node.
    132
    133        Returns:
    134            Boolean indicating if child node can be deleted.
    135
    136        Raises:
    137            ValueError: If the word is not found.
    138        """
    139        if i == len(word):
    140            return True
    141
    142        if current is None:
    143            current = self.root
    144            word = word + Trie.end_marker
    145
    146        char = word[i]
    147        if char not in current.children:
    148            return False
    149
    150        next_node = current.children[char]
    151
    152        del current.children[char]
    153
    154        self.delete(word, i + 1, next_node)
    155
    156        return False
    157
    158    def list_words(self) -> list:
    159        """
    160        Return a list of all words in the trie.
    161        """
    162        return self.build_word_list(self.root, "", [])
    163
    164    def build_word_list(self, node: TrieNode=None, word: str = "", words=None):
    165        """
    166        Helper method to return a list of words given a starting node.
    167
    168        Args:
    169            node (TrieNode): The current trie node.
    170            word (str): The word to build after each recursive call.
    171            words: The list of words.
    172
    173        Returns:
    174            List of words that begin with a given prefix.
    175
    176        Raises:
    177            ValueError: If the word is not found.
    178        """
    179        if words is None:
    180            words = []
    181
    182        if node is None:
    183            current = self.root
    184        else:
    185            current = node
    186
    187        for key, node in sorted(current.children.items()):
    188            if key == Trie.end_marker:
    189                words.append(word)
    190            else:
    191                self.build_word_list(node, word + key, words)
    192        return words
    193
    194    def autocomplete(self, prefix: str):
    195        """
    196        Return a list of words that begin with a given prefix.
    197
    198        Args:
    199            prefix (str): The prefix to search for.
    200
    201        Returns:
    202            List of words that begin with a given prefix.
    203            None if there are no matching words.
    204
    205        Raises:
    206            ValueError: If no words are found.
    207        """
    208        current = self.search_node(prefix)
    209        if current is None:
    210            return None #self.list_words(current, "")
    211        return self.build_word_list(current, prefix)
    212
    213    def suggest(self, prefix: str):
    214        """
    215        Return a list of close words with a given prefix.
    216
    217        Args:
    218            prefix (str): The prefix to search for.
    219
    220        Returns:
    221            List of words that are similar to a given prefix.
    222            None if there are no matching words.
    223
    224        Raises:
    225            ValueError: If the word is not found.
    226        """
    227        if prefix is None or len(prefix) == 0:
    228            return None
    229        suggestions = self.autocomplete(prefix)
    230        if suggestions is None or len(suggestions) == 0:
    231            return self.suggest(prefix[:-1])
    232        else:
    233            return suggestions
    234
    235    def copy_node(self, node: TrieNode):
    236        """
    237        Recursively deep copy a node and its children.
    238
    239        Args:
    240            node (TrieNode): The node to copy.
    241
    242        Returns:
    243            A deep copy of the node.
    244        """
    245        if not node:
    246            return
    247        new_node = TrieNode()
    248        for char, child in node.children.items():
    249            new_node.children[char] = self.copy_node(child)
    250        return new_node
    251
    252    def copy(self):
    253        """
    254        Create a deep copy of the Trie.
    255
    256        Returns:
    257            A deep copy of the trie.
    258        """
    259        new_trie = Trie()
    260        new_trie.root = self.copy_node(self.root)
    261        return new_trie

class TrieNode: View Source

     4class TrieNode:
     5    """
     6    A trie node implementation.
     7    """
     8    def __init__(self):
     9        #: a dictionary of key (character) ->value (references)
    10        self.children = {}

A trie node implementation.

children
class Trie: View Source

     12class Trie:
     13    """
     14    A trie implementation.
     15    """
     16    #: end marker
     17    end_marker = "*"
     18
     19    def __init__(self):
     20        """
     21        Initialize a trie.
     22        """
     23        #: root of the trie
     24        self.root = TrieNode()
     25
     26    def insert(self, word: str):
     27        """
     28        Insert a word into a trie.
     29
     30        Args:
     31            word (str): The word to insert.
     32
     33        Returns:
     34            None
     35        """
     36        current = self.root
     37        for c in word:
     38            if c not in current.children:
     39                current.children[c] = TrieNode()
     40
     41            current = current.children[c]
     42        current.children[Trie.end_marker] = None
     43
     44    def search(self, word: str) -> bool:
     45        """
     46        Search for a word in a trie.
     47
     48        Args:
     49            word (str): The word to search for.
     50
     51        Returns:
     52            Boolean on whether the complete word is found.
     53        """
     54        if len(word) == 0:
     55            return False
     56
     57        current = self.root
     58        for c in word + Trie.end_marker:
     59            if c not in current.children:
     60                return False
     61            current = current.children[c]
     62        return True
     63
     64    def search_node(self, substr: str) -> TrieNode:
     65        """
     66        Search for a substring in a trie.
     67
     68        Args:
     69            substr (str): A substring to search for.
     70
     71        Returns:
     72            TriedNode where the substring begins
     73            None if the substring is not found
     74        """
     75        if len(substr) == 0:
     76            return None
     77
     78        current = self.root
     79        for c in substr:
     80            if c not in current.children:
     81                return None
     82            current = current.children[c]
     83        return current
     84
     85    def delete(self, word: str, i: int=0, current: TrieNode=None) -> bool:
     86        """
     87        Delete a word from the trie.
     88
     89        Args:
     90            word (str): The word to delete.
     91            i (int): The index of character.
     92            current (TrieNode): The current node.
     93
     94        Returns:
     95            Boolean indicating if child node can be deleted.
     96
     97        Raises:
     98            ValueError: If the word is not found.
     99        """
    100        if i == len(word):
    101            return True
    102
    103        # set up the beginning of delete
    104        if current is None:
    105            current = self.root
    106            word = word + Trie.end_marker
    107
    108        char = word[i]
    109
    110        # character is not found, so word is invalid and return False
    111        if char not in current.children:
    112            return False
    113
    114        next_node = current.children[char]
    115        should_delete_ref = self.delete(word, i + 1, next_node)
    116
    117        # delete the child reference
    118        if should_delete_ref:
    119            del current.children[char]
    120
    121            # return True if there are no more children
    122            return len(current.children) == 0
    123        return False
    124
    125    def delete_preorder(self, word: str, i: int=0, current: TrieNode=None):
    126        """
    127        Delete a word using preorder (Do not use! For demonstration purposes only).
    128
    129        Args:
    130            word (str): The word to delete.
    131            i (int): The index of character.
    132            current (TrieNode): The current node.
    133
    134        Returns:
    135            Boolean indicating if child node can be deleted.
    136
    137        Raises:
    138            ValueError: If the word is not found.
    139        """
    140        if i == len(word):
    141            return True
    142
    143        if current is None:
    144            current = self.root
    145            word = word + Trie.end_marker
    146
    147        char = word[i]
    148        if char not in current.children:
    149            return False
    150
    151        next_node = current.children[char]
    152
    153        del current.children[char]
    154
    155        self.delete(word, i + 1, next_node)
    156
    157        return False
    158
    159    def list_words(self) -> list:
    160        """
    161        Return a list of all words in the trie.
    162        """
    163        return self.build_word_list(self.root, "", [])
    164
    165    def build_word_list(self, node: TrieNode=None, word: str = "", words=None):
    166        """
    167        Helper method to return a list of words given a starting node.
    168
    169        Args:
    170            node (TrieNode): The current trie node.
    171            word (str): The word to build after each recursive call.
    172            words: The list of words.
    173
    174        Returns:
    175            List of words that begin with a given prefix.
    176
    177        Raises:
    178            ValueError: If the word is not found.
    179        """
    180        if words is None:
    181            words = []
    182
    183        if node is None:
    184            current = self.root
    185        else:
    186            current = node
    187
    188        for key, node in sorted(current.children.items()):
    189            if key == Trie.end_marker:
    190                words.append(word)
    191            else:
    192                self.build_word_list(node, word + key, words)
    193        return words
    194
    195    def autocomplete(self, prefix: str):
    196        """
    197        Return a list of words that begin with a given prefix.
    198
    199        Args:
    200            prefix (str): The prefix to search for.
    201
    202        Returns:
    203            List of words that begin with a given prefix.
    204            None if there are no matching words.
    205
    206        Raises:
    207            ValueError: If no words are found.
    208        """
    209        current = self.search_node(prefix)
    210        if current is None:
    211            return None #self.list_words(current, "")
    212        return self.build_word_list(current, prefix)
    213
    214    def suggest(self, prefix: str):
    215        """
    216        Return a list of close words with a given prefix.
    217
    218        Args:
    219            prefix (str): The prefix to search for.
    220
    221        Returns:
    222            List of words that are similar to a given prefix.
    223            None if there are no matching words.
    224
    225        Raises:
    226            ValueError: If the word is not found.
    227        """
    228        if prefix is None or len(prefix) == 0:
    229            return None
    230        suggestions = self.autocomplete(prefix)
    231        if suggestions is None or len(suggestions) == 0:
    232            return self.suggest(prefix[:-1])
    233        else:
    234            return suggestions
    235
    236    def copy_node(self, node: TrieNode):
    237        """
    238        Recursively deep copy a node and its children.
    239
    240        Args:
    241            node (TrieNode): The node to copy.
    242
    243        Returns:
    244            A deep copy of the node.
    245        """
    246        if not node:
    247            return
    248        new_node = TrieNode()
    249        for char, child in node.children.items():
    250            new_node.children[char] = self.copy_node(child)
    251        return new_node
    252
    253    def copy(self):
    254        """
    255        Create a deep copy of the Trie.
    256
    257        Returns:
    258            A deep copy of the trie.
    259        """
    260        new_trie = Trie()
    261        new_trie.root = self.copy_node(self.root)
    262        return new_trie

A trie implementation.

Trie() View Source

    19    def __init__(self):
    20        """
    21        Initialize a trie.
    22        """
    23        #: root of the trie
    24        self.root = TrieNode()

Initialize a trie.

end_marker = '*'
root
def insert(self, word: str): View Source

    26    def insert(self, word: str):
    27        """
    28        Insert a word into a trie.
    29
    30        Args:
    31            word (str): The word to insert.
    32
    33        Returns:
    34            None
    35        """
    36        current = self.root
    37        for c in word:
    38            if c not in current.children:
    39                current.children[c] = TrieNode()
    40
    41            current = current.children[c]
    42        current.children[Trie.end_marker] = None

Insert a word into a trie.

Args: word (str): The word to insert.

Returns: None

def search(self, word: str) -> bool: View Source

    44    def search(self, word: str) -> bool:
    45        """
    46        Search for a word in a trie.
    47
    48        Args:
    49            word (str): The word to search for.
    50
    51        Returns:
    52            Boolean on whether the complete word is found.
    53        """
    54        if len(word) == 0:
    55            return False
    56
    57        current = self.root
    58        for c in word + Trie.end_marker:
    59            if c not in current.children:
    60                return False
    61            current = current.children[c]
    62        return True

Search for a word in a trie.

Args: word (str): The word to search for.

Returns: Boolean on whether the complete word is found.

def search_node(self, substr: str) -> TrieNode: View Source

    64    def search_node(self, substr: str) -> TrieNode:
    65        """
    66        Search for a substring in a trie.
    67
    68        Args:
    69            substr (str): A substring to search for.
    70
    71        Returns:
    72            TriedNode where the substring begins
    73            None if the substring is not found
    74        """
    75        if len(substr) == 0:
    76            return None
    77
    78        current = self.root
    79        for c in substr:
    80            if c not in current.children:
    81                return None
    82            current = current.children[c]
    83        return current

Search for a substring in a trie.

Args: substr (str): A substring to search for.

Returns: TriedNode where the substring begins None if the substring is not found

def delete(self, word: str, i: int = 0, current: TrieNode = None) -> bool: View Source

     85    def delete(self, word: str, i: int=0, current: TrieNode=None) -> bool:
     86        """
     87        Delete a word from the trie.
     88
     89        Args:
     90            word (str): The word to delete.
     91            i (int): The index of character.
     92            current (TrieNode): The current node.
     93
     94        Returns:
     95            Boolean indicating if child node can be deleted.
     96
     97        Raises:
     98            ValueError: If the word is not found.
     99        """
    100        if i == len(word):
    101            return True
    102
    103        # set up the beginning of delete
    104        if current is None:
    105            current = self.root
    106            word = word + Trie.end_marker
    107
    108        char = word[i]
    109
    110        # character is not found, so word is invalid and return False
    111        if char not in current.children:
    112            return False
    113
    114        next_node = current.children[char]
    115        should_delete_ref = self.delete(word, i + 1, next_node)
    116
    117        # delete the child reference
    118        if should_delete_ref:
    119            del current.children[char]
    120
    121            # return True if there are no more children
    122            return len(current.children) == 0
    123        return False

Delete a word from the trie.

Args: word (str): The word to delete. i (int): The index of character. current (TrieNode): The current node.

Returns: Boolean indicating if child node can be deleted.

Raises: ValueError: If the word is not found.

def delete_preorder(self, word: str, i: int = 0, current: TrieNode = None): View Source

    125    def delete_preorder(self, word: str, i: int=0, current: TrieNode=None):
    126        """
    127        Delete a word using preorder (Do not use! For demonstration purposes only).
    128
    129        Args:
    130            word (str): The word to delete.
    131            i (int): The index of character.
    132            current (TrieNode): The current node.
    133
    134        Returns:
    135            Boolean indicating if child node can be deleted.
    136
    137        Raises:
    138            ValueError: If the word is not found.
    139        """
    140        if i == len(word):
    141            return True
    142
    143        if current is None:
    144            current = self.root
    145            word = word + Trie.end_marker
    146
    147        char = word[i]
    148        if char not in current.children:
    149            return False
    150
    151        next_node = current.children[char]
    152
    153        del current.children[char]
    154
    155        self.delete(word, i + 1, next_node)
    156
    157        return False

Delete a word using preorder (Do not use! For demonstration purposes only).

Args: word (str): The word to delete. i (int): The index of character. current (TrieNode): The current node.

Returns: Boolean indicating if child node can be deleted.

Raises: ValueError: If the word is not found.

def list_words(self) -> list: View Source

    159    def list_words(self) -> list:
    160        """
    161        Return a list of all words in the trie.
    162        """
    163        return self.build_word_list(self.root, "", [])

Return a list of all words in the trie.

def build_word_list(self, node: TrieNode = None, word: str = '', words=None): View Source

    165    def build_word_list(self, node: TrieNode=None, word: str = "", words=None):
    166        """
    167        Helper method to return a list of words given a starting node.
    168
    169        Args:
    170            node (TrieNode): The current trie node.
    171            word (str): The word to build after each recursive call.
    172            words: The list of words.
    173
    174        Returns:
    175            List of words that begin with a given prefix.
    176
    177        Raises:
    178            ValueError: If the word is not found.
    179        """
    180        if words is None:
    181            words = []
    182
    183        if node is None:
    184            current = self.root
    185        else:
    186            current = node
    187
    188        for key, node in sorted(current.children.items()):
    189            if key == Trie.end_marker:
    190                words.append(word)
    191            else:
    192                self.build_word_list(node, word + key, words)
    193        return words

Helper method to return a list of words given a starting node.

Args: node (TrieNode): The current trie node. word (str): The word to build after each recursive call. words: The list of words.

Returns: List of words that begin with a given prefix.

Raises: ValueError: If the word is not found.

def autocomplete(self, prefix: str): View Source

    195    def autocomplete(self, prefix: str):
    196        """
    197        Return a list of words that begin with a given prefix.
    198
    199        Args:
    200            prefix (str): The prefix to search for.
    201
    202        Returns:
    203            List of words that begin with a given prefix.
    204            None if there are no matching words.
    205
    206        Raises:
    207            ValueError: If no words are found.
    208        """
    209        current = self.search_node(prefix)
    210        if current is None:
    211            return None #self.list_words(current, "")
    212        return self.build_word_list(current, prefix)

Return a list of words that begin with a given prefix.

Args: prefix (str): The prefix to search for.

Returns: List of words that begin with a given prefix. None if there are no matching words.

Raises: ValueError: If no words are found.

def suggest(self, prefix: str): View Source

    214    def suggest(self, prefix: str):
    215        """
    216        Return a list of close words with a given prefix.
    217
    218        Args:
    219            prefix (str): The prefix to search for.
    220
    221        Returns:
    222            List of words that are similar to a given prefix.
    223            None if there are no matching words.
    224
    225        Raises:
    226            ValueError: If the word is not found.
    227        """
    228        if prefix is None or len(prefix) == 0:
    229            return None
    230        suggestions = self.autocomplete(prefix)
    231        if suggestions is None or len(suggestions) == 0:
    232            return self.suggest(prefix[:-1])
    233        else:
    234            return suggestions

Return a list of close words with a given prefix.

Args: prefix (str): The prefix to search for.

Returns: List of words that are similar to a given prefix. None if there are no matching words.

Raises: ValueError: If the word is not found.

def copy_node(self, node: TrieNode): View Source

    236    def copy_node(self, node: TrieNode):
    237        """
    238        Recursively deep copy a node and its children.
    239
    240        Args:
    241            node (TrieNode): The node to copy.
    242
    243        Returns:
    244            A deep copy of the node.
    245        """
    246        if not node:
    247            return
    248        new_node = TrieNode()
    249        for char, child in node.children.items():
    250            new_node.children[char] = self.copy_node(child)
    251        return new_node

Recursively deep copy a node and its children.

Args: node (TrieNode): The node to copy.

Returns: A deep copy of the node.

def copy(self): View Source

    253    def copy(self):
    254        """
    255        Create a deep copy of the Trie.
    256
    257        Returns:
    258            A deep copy of the trie.
    259        """
    260        new_trie = Trie()
    261        new_trie.root = self.copy_node(self.root)
    262        return new_trie

Create a deep copy of the Trie.

Returns: A deep copy of the trie.