dsa.trie
Module containing trie class.
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
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.
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.
19 def __init__(self): 20 """ 21 Initialize a trie. 22 """ 23 #: root of the trie 24 self.root = TrieNode()
Initialize a trie.
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
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.
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
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.
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.
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.
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.
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.
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.
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.