Exemplo n.º 1
0
 def test_child_methods(self):
     # Create node 'A' and verify it does not have any children
     node_A = PrefixTreeNode('A')
     assert node_A.num_children() == 0
     assert node_A.has_child('B') is False
     # Verify getting child from node 'A' raises error
     with self.assertRaises(ValueError):
         node_A.get_child('B')
     # Create node 'B' and add it as child to node 'A'
     node_B = PrefixTreeNode('B')
     node_A.add_child('B', node_B)
     # Verify node 'A' has node 'B' as child
     assert node_A.num_children() == 1
     assert node_A.has_child('B') is True
     assert node_A.get_child('B') is node_B
     # Verify adding node 'B' as child to node 'A' again raises error
     with self.assertRaises(ValueError):
         node_A.add_child('B', node_B)
     # Create node 'C' and add it as another child to node 'A'
     node_C = PrefixTreeNode('C')
     node_A.add_child('C', node_C)
     # Verify node 'A' has both nodes 'B' and 'C' as children
     assert node_A.num_children() == 2
     assert node_A.has_child('B') is True
     assert node_A.has_child('C') is True
     assert node_A.get_child('C') is node_C
     # Verify adding node 'C' as child to node 'A' again raises error
     with self.assertRaises(ValueError):
         node_A.add_child('C', node_C)
Exemplo n.º 2
0
class PrefixTree:
    """PrefixTree: A multi-way prefix tree that stores strings with efficient
    methods to insert a string into the tree, check if it contains a matching
    string, and retrieve all strings that start with a given prefix string.
    Time complexity of these methods depends only on the number of strings
    retrieved and their maximum length (size and height of subtree searched),
    but is independent of the number of strings stored in the prefix tree, as
    its height depends only on the length of the longest string stored in it.
    This makes a prefix tree effective for spell-checking and autocompletion.
    Each string is stored as a sequence of characters along a path from the
    tree's root node to a terminal node that marks the end of the string."""

    # Constant for the start character stored in the prefix tree's root node
    START_CHARACTER = ''

    def __init__(self, strings=None):
        """Initialize this prefix tree and insert the given strings, if any."""
        # Create a new root node with the start character
        self.root = PrefixTreeNode(PrefixTree.START_CHARACTER)
        # Count the number of strings inserted into the tree
        self.size = 0
        # Insert each string, if any were given
        if strings is not None:
            for string in strings:
                self.insert(string)

    def __repr__(self):
        """Return a string representation of this prefix tree."""
        return f'PrefixTree({self.strings()!r})'

    def is_empty(self):
        """Return True if this prefix tree is empty (contains no strings)."""
        return self.size == 0

    def contains(self, string):
        """Return True if this prefix tree contains the given string."""
        current_node = self.root

        for char in string:
            # using try and except here because get_child method raises a ValueError if not found
            try:
                current_node = current_node.get_child(char)
            except:
                return False

        return current_node.terminal

    def insert(self, string):
        """Insert the given string into this prefix tree."""
        current_node = self.root

        for char in string:
            # we add a node if not found in current node's children
            if not current_node.has_child(char):
                new_node = PrefixTreeNode(char)
                current_node.add_child(new_node)
            current_node = current_node.get_child(char)

        # only want to increment size if terminal is not False
        if not current_node.is_terminal():
            self.size += 1
            current_node.terminal = True

    def _find_node(self, string):
        """Return a pair containing the deepest node in this prefix tree that
        matches the longest prefix of the given string and the node's depth.
        The depth returned is equal to the number of prefix characters matched.
        Search is done iteratively with a loop starting from the root node."""
        # Match the empty string
        if len(string) == 0:
            return self.root, 0
        # Start with the root node
        node = self.root
        depth = 0

        for char in string:
            # runs the entirety of the string to get the last node; we are assuming
            # that the string is a valid word in tree
            if node.has_child(char):
                node = node.get_child(char)
                depth += 1
        return node, depth

    def complete(self, prefix):
        """Return a list of all strings stored in this prefix tree that start
        with the given prefix string."""
        # Create a list of completions in prefix tree
        completions = []
        # find last(deepest) node
        node = self._find_node(prefix)[0]

        # if node is terminal we append the prefix because the last node of the prefix is terminal it's a valid string
        if node.is_terminal():
            completions.append(prefix)

        # if prefix != '':
        if not self.root.has_child(prefix[0]):
            return completions

        for child in node.children:
            # traverse on each child with the prefix as the prefix plus the child
            # node character, and the completion is appending to the completions array
            self._traverse(child, (prefix + child.character),
                           completions.append)

        return completions

    def strings(self):
        """Return a list of all strings stored in this prefix tree."""
        # Create a list of all strings in prefix tree
        all_strings = []
        # since we want all valid words or strings in the tree we start with the root node
        # with the prefix as empty or '', and then we append that to all_strings as our completion
        self._traverse(self.root, '', all_strings.append)
        return all_strings

        # OR but need to activate line 98
        # return self.complete('')

    # helper function to traverse the nodes
    def _traverse(self, node, prefix, visit):
        """Traverse this prefix tree with recursive depth-first traversal.
        Start at the given node with the given prefix representing its path in
        this prefix tree and visit each node with the given visit function."""
        # like base case
        if node.is_terminal():
            visit(prefix
                  )  # append function, and append our prefix based on traverse

        if len(node.children) > 0:
            for child in node.children:
                # we are recursively calling this function until we reach terminal or when children doesn't exist
                self._traverse(child, (prefix + child.character), visit)
class PrefixTree:
    """PrefixTree: A multi-way prefix tree that stores strings with efficient
    methods to insert a string into the tree, check if it contains a matching
    string, and retrieve all strings that start with a given prefix string.
    Time complexity of these methods depends only on the number of strings
    retrieved and their maximum length (size and height of subtree searched),
    but is independent of the number of strings stored in the prefix tree, as
    its height depends only on the length of the longest string stored in it.
    This makes a prefix tree effective for spell-checking and autocompletion.
    Each string is stored as a sequence of characters along a path from the
    tree's root node to a terminal node that marks the end of the string."""

    # Constant for the start character stored in the prefix tree's ROOT NODE

    START_CHARACTER = ''

    def __init__(self, strings=None):
        """Initialize this prefix tree and insert the given strings, if any."""
        # Create a new root node with the start character
        self.root = PrefixTreeNode(PrefixTree.START_CHARACTER)
        # Count the number of strings inserted into the tree
        self.size = 0
        # Insert each string, if any were given
        if strings is not None:
            for string in strings:
                self.insert(string)

    def __repr__(self):
        """Return a string representation of this prefix tree."""
        return f'PrefixTree({self.strings()!r})'

    def is_empty(self):
        """Return True if this prefix tree is empty (contains no strings)."""
        return self.root.num_children() == 0
        # return self.size == 0 #maybe?

    def contains(self, string):
        """Return True if this prefix tree contains the given string."""
        return self.root.has_child(string)

    def insert(self, string):
        """Insert the given string into this prefix tree."""
        self.root.add_child(string)

    def _find_node(self, string):
        """Return a tuple containing the node that terminates the given string
        in this prefix tree and the node's depth, or if the given string is not
        completely found, return None and the depth of the last matching node.
        Search is done iteratively with a loop starting from the root node."""
        # Match the empty string
        if len(string) == 0:
            return self.root, 0
        # Start with the root node
        node = self.root
        # TODO

    def complete(self, prefix):
        """Return a list of all strings stored in this prefix tree that start
        with the given prefix string."""
        # Create a list of completions in prefix tree
        completions = []
        # TODO

    def strings(self):
        """Return a list of all strings stored in this prefix tree."""
        # Create a list of all strings in prefix tree
        all_strings = []
        # TODO

    def _traverse(self, node, prefix, visit):
        """Traverse this prefix tree with recursive depth-first traversal.
class PrefixTree:
    """PrefixTree: A multi-way prefix tree that stores strings with efficient
    methods to insert a string into the tree, check if it contains a matching
    string, and retrieve all strings that start with a given prefix string.
    Time complexity of these methods depends only on the number of strings
    retrieved and their maximum length (size and height of subtree searched),
    but is independent of the number of strings stored in the prefix tree, as
    its height depends only on the length of the longest string stored in it.
    This makes a prefix tree effective for spell-checking and autocompletion.
    Each string is stored as a sequence of characters along a path from the
    tree's root node to a terminal node that marks the end of the string."""

    # Constant for the start character stored in the prefix tree's root node
    START_CHARACTER = ''

    def __init__(self, strings=None):
        """Initialize this prefix tree and insert the given strings, if any."""
        # Create a new root node with the start character
        self.root = PrefixTreeNode(PrefixTree.START_CHARACTER)
        # Count the number of strings inserted into the tree
        self.size = 0
        # Insert each string, if any were given
        if strings is not None:
            for string in strings:
                self.insert(string)

    def __repr__(self):
        """Return a string representation of this prefix tree."""
        return f'PrefixTree({self.strings()!r})'

    def is_empty(self):
        """Return True if this prefix tree is empty (contains no strings)."""
        # TODO
        return self.size == 0

    def contains(self, string):
        """Return True if this prefix tree contains the given string."""
        # TODO
        current_node = self.root
        for char in string:
            try:
                current_node = current_node.get_child(char)
            except:
                return False
        return current_node.terminal

    def insert(self, string):
        """Insert the given string into this prefix tree."""
        # TODO
        current = self.root
        #"h e l l o"
        for i in range(len(string)):

          #if current does not have children:
            #insert new node with current #char in string
            #create a new node
            #add it as a child of current node
          #if there is a child the child is the letter of the string
          #current = that child
        #when I'm at the end of the string I want to make the last character terminal
          print(string[i])
          if not current.has_child(string[i]):
            new_node = PrefixTreeNode(string[i])
            current.add_child(string[i],new_node)
            print("Current", current)
          current = current.get_child(string[i])
          print("Change to next")
        print("End", current)
        if current.terminal == False:
            self.size += 1
            current.terminal = True

    def _find_node(self, string):
        """Return a pair containing the deepest node in this prefix tree that
        matches the longest prefix of the given string and the node's depth.
        The depth returned is equal to the number of prefix characters matched.
        Search is done iteratively with a loop starting from the root node."""
        # Match the empty string
        if len(string) == 0:
            return self.root, 0
        # Start with the root node
        node = self.root
        depth = 0
        for letter in string:

          if node.has_child(letter):
            node = node.get_child(letter)
            depth += 1

        return node, depth

    def complete(self, prefix):
        """Return a list of all strings stored in this prefix tree that start
        with the given prefix string."""
        # Create a list of completions in prefix tree
        completions = []

        #get that node object using find prefix node
        node = self._find_node(prefix)[0]

        #if it's terminal the just append that prefix to completions list
        if node.terminal == True: 
            completions.append(prefix)

        # Return the empty list
        if self.root.has_child(prefix[0]) == False:
            return completions

        #if not terminal we need to traverse
        # for every child of this node
        # get a child node of this node
        for child in node.children:
            # use traverse on the child with the prefix + , completions.append
            self._traverse(child, prefix + child.character, completions.append)
        return completions

        

    def strings(self):
        """Return a list of all strings stored in this prefix tree."""
        # Create a list of all strings in prefix tree
        all_strings = []
        # TODO
        self._traverse(self.root, '', all_strings.append)
        return all_strings

    # Helper function
    def _traverse(self, node, prefix, visit):
        """Traverse this prefix tree with recursive depth-first traversal.
        Start at the given node with the given prefix representing its path in
        this prefix tree and visit each node with the given visit function."""
        # TODO
        #if the node is terminal 
        #call visit
        if node.is_terminal():
          visit(prefix)

        #if the node has children (can be terminal AND have children) 
        #we need to loop children
        #call traverse on the children
        if len(node.children) > 0:
          for child in node.children:
            self._traverse(child, prefix+child.character, visit)
Exemplo n.º 5
0
class PrefixTree:
    """PrefixTree: A multi-way prefix tree that stores strings with efficient
    methods to insert a string into the tree, check if it contains a matching
    string, and retrieve all strings that start with a given prefix string.
    Time complexity of these methods depends only on the number of strings
    retrieved and their maximum length (size and height of subtree searched),
    but is independent of the number of strings stored in the prefix tree, as
    its height depends only on the length of the longest string stored in it.
    This makes a prefix tree effective for spell-checking and autocompletion.
    Each string is stored as a sequence of characters along a path from the
    tree's root node to a terminal node that marks the end of the string."""

    # Constant for the start character stored in the prefix tree's root node
    START_CHARACTER = ''

    def __init__(self, strings=None):
        """Initialize this prefix tree and insert the given strings, if any."""
        # Create a new root node with the start character
        self.root = PrefixTreeNode(PrefixTree.START_CHARACTER)
        # Count the number of strings inserted into the tree
        self.size = 0
        # Insert each string, if any were given
        if strings is not None:
            for string in strings:
                self.insert(string)

    def __repr__(self):
        """Return a string representation of this prefix tree."""
        return f'PrefixTree({self.strings()!r})'

    def is_empty(self):
        """Return True if this prefix tree is empty (contains no strings)."""
        
        return self.size == 0

    def contains(self, string):
        """Return True if this prefix tree contains the given string."""
        
        node = self.root
        for character in string:
            # if that node has our child we set the node = to that node so we
            # can continue down the Trie
            if node.has_child(character):
                child_node = node.get_child(character)
                node = child_node
            else:
                node.is_terminal()
        return node.is_terminal()

    def insert(self, string):
        """Insert the given string into this prefix tree."""
        # TODO
        current = self.root
        
        for i in range(len(string)):
            if not current.has_child(string[i]):
                new_node = PrefixTreeNode(string[i])
                current.add_child(string[i], new_node)
            current = current.get_child(string[i])
        if current.terminal == False:
            self.size += 1
            current.terminal = True

    def _find_node(self, string):
        """Return a pair containing the deepest node in this prefix tree that
        matches the longest prefix of the given string and the node's depth.
        The depth returned is equal to the number of prefix characters matched.
        Search is done iteratively with a loop starting from the root node."""
        # Match the empty string
        if len(string) == 0:
            return self.root, 0
        # Start with the root node
        node = self.root
        depth = 0 
        
        for letter in string:
            # looky looky for child
            if node.has_child(letter):
                # found child now my node = to where child is and add to depth 
                node = node.get_child(letter)
                depth += 1
        return node, depth


    def complete(self, prefix):
        """Return a list of all strings stored in this prefix tree that start
        with the given prefix string."""
        # Create a list of completions in prefix tree
        completions = []
        #get that node object using find prefix node
        node = self._find_node(prefix)[0]

        # if terminal append to completions 
        if node.terminal == True: 
            completions.append(prefix)
                
        if self.root.has_child(prefix[0]) == False:
            return completions

        # traverse Trie all recursive like 
        for child in node.children:
            self._traverse(child, prefix + child.character, completions.append)
        return completions
            

    def strings(self):
        """Return a list of all strings stored in this prefix tree."""
        # Create a list of all strings in prefix tree
        all_strings = []
        
        self._traverse(self.root, '', all_strings.append)
        return all_strings

    def _traverse(self, node, prefix, visit):
        """Traverse this prefix tree with recursive depth-first traversal.
        Start at the given node with the given prefix representing its path in
        this prefix tree and visit each node with the given visit function."""
        if node.is_terminal():
            visit(prefix)
        
        if len(node.children) > 0:
            for child in node.children:
                self._traverse(child, prefix+child.character, visit)