Using Recursion to add to a trie - python-3.x

I have been learning about the Trie structure through python. What is a little bit different about his trie compared to other tries is the fact that we are trying to implement a counter into every node of the trie in order to do an autocomplete (that is the final hope for the project). So far, I decided that having a recursive function to put the letter into a list of dictionaries would be a good idea.
Final Product (Trie):
Trie = {"value":"*start"
"count":1
"children":["value":"t"
"count":1
"children":["value":"e"
"count":1
"children":[...]
I know that a recursion would be very useful as it is just adding letters to the list, however, I can't figure out how to construct the basic function and how to tell the computer to refer to the last part of the dictionary without writing out
Trie["children"]["children"]["children"]["children"]
a bunch of times. Can you guys please give me some ideas as of how to construct the function?
--Thanks

Perhaps you only want to store a counter in the end-of-word node. (I always use a separate node for this!). So given a prefix, you work your way down the Trie, then perform a traversal to find the end-of-word nodes below your prefix, pushing them into a binary tree by their counter value. Once you have all the end-of-word nodes, you perform a post-order traversal to collect the end-of-word nodes with the largest counters - however many you decide. With each of these collected end-of-word nodes, you can obtain a list of candidate words most likely to fit the prefix! From just the end-of-word node, and storing the parent node in each node, you zoom up to the root to re-assemble the word!

The Trie itself containing the root node and insert/find functions
class Trie:
def __init__(self):
## Initialize this Trie (add a root node)
self.root = TrieNode()
def insert(self, word):
## Add a word to the Trie
current_node = self.root
for char in word:
if char not in current_node.children:
current_node.insert(char)
current_node = current_node.children[char]
current_node.is_word = True
def find(self, prefix):
## Find the Trie node that represents this prefix
current_node = self.root
for char in prefix:
if char not in current_node.children:
return None
current_node = current_node.children[char]
return current_node
This is a class method to implement autocomplete with trie.
class TrieNode:
def __init__(self):
## Initialize this node in the Trie
self.is_word = False
self.children = {}
self.results = []
def insert(self, char):
## Add a child node in this Trie
self.children[char] = TrieNode()
def suffixes(self, suffix=''):
## Recursive function that collects the suffix for
## all complete words below this point
for char, node in self.children.items():
if node.is_word:
self.results.append(suffix + char)
if node.children:
self.results += node.suffixes(suffix + char)
results = self.results.copy()
self.results = []
return list(set(results))
Solutions can be obtained by using the following command.
MyTrie = Trie()
wordList = ["ant", "anthology", "antagonist", "antonym",
"fun", "function", "factory", "trie", "trigger", "trigonometry", "tripod"]
for word in wordList:
MyTrie.insert(word)
MyTrie.find('a').suffixes()
MyTrie.find('f').suffixes()
MyTrie.find('t').suffixes()
MyTrie.find('tri').suffixes()
MyTrie.find('anto').suffixes()

Related

How does nodes list get built up in __repr__(self) method while invoking other methods in class LinkedList

I'm trying to understand how the __repr__ method gets called and the nodes list gets updated during invocation of class's other methods. Sorry if my terminology isn't correct. I've only included the prepend and append methods for brevity. It looks to me like the list "nodes" would get initialized over and over again. Thanks!
class Node:
"""A node in a singly-linked list"""
def __init__(self, dataval=None, nextval=None):
self.dataval = dataval
self.nextval = nextval
def __repr__(self):
return repr(self.dataval)
class LinkedList:
def __init__(self):
"""Creating a new singly-linked list."""
self.head = None
def __repr__(self):
"""Creating a string representation of the data field in a list"""
nodes = []
curr = self.head
while curr:
nodes.append(repr(curr))
curr = curr.nextval
return '[' + '->'.join(nodes) + ']'
def prepend(self, dataval):
"""Insert a new element at the beginning of the list."""
self.head = Node(dataval=dataval, nextval=self.head)
def append(self, dataval):
"""Insert a new element at the end of the list."""
if not self.head:
self.head = Node(dataval=dataval)
return
curr = self.head
while curr.nextval:
curr = curr.nextval
curr.nextval = Node(dataval=dataval)
The code works, I just don't understand quite how it works. It's from an online course about data structures.
Inside the LinkedList's __repr__ you create a new local nodes list, you call the __repr__ of all of the nodes by passing on their nextval attribute and add their string representation to the list, and then concatenate the list together using '->'.join().
You can visualize it using python tutor starting from step 41.
You seem to be confused about the call to append in __repr__: that is not a call to the append method in your class. It is calling append on a native list type. The two methods happen to have the same name, and I guess that's where the mixup comes from.
The __repr__ method creates a list (not a LinkedList) and populates it with the string representations of the nodes in the linked list. Then it joins those strings to one string and returns it. During this process no other method of the LinkedList class is called.
__repr__ isn't called in the prepend and append routines. The nodes list in LinkedList.__rep__ is only there temporarily to hold the nodes while its return value is built. In fact, your classes don't use list to hold nodes at all. LinkedList has a reference to the first Node in head and each Node has a reference to the next node in nextval.

Original linked list and its copy, both are getting updated simultaneously when I am making changes only to one?

I am trying to solve LeetCode problem 19. Remove Nth Node From End of List:
Given the head of a linked list, remove the nth node from the end of the list and return its head.
Here is my solution.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
Length=1
curr=head
while curr.next!=None:
Length=Length+1
curr=curr.next
count = 0
curr=head
if Length == n:
return head.next
while count < Length-n:
if count == Length-n-1:
curr.next = curr.next.next
break
count = count+1
curr = curr.next
return head
My question is, when I am making all the changes to curr (copy of the original), why are those changes getting reflected in the head (original) as well?
The thing is that curr is not a copy of the original (node or list). It is a name you give for a given node that is in the original list. As long as you don't really create new nodes (calling Node(value)), you cannot be referencing anything else than the nodes in the original list. curr references one node after the other in the original list and at a certain point mutates a next reference. This happens in the original list. There is no other list at play.
The only time that the caller does not see a change in the original list, is when you don't return head, but head.next. In that case head still refers to the original list, while the returned node reference, is skipping the first node. But both lists have all nodes in common except the node referenced by head.

Python recursive function to create generic tree [duplicate]

This question already has answers here:
Why does my recursive function return None?
(4 answers)
Closed 7 months ago.
I'm trying to create a tree with n children for each node.
Problem is, when I try to implement this using a recursive function, I ends up with multiple recursive calls so I can't have a single return statement, hence having None as a final result.
Here is my piece of code :
def recursive_func(tree, n):
if n == 0:
return tree
else:
permutations = get_permutations()
for permutation in permutations:
child_tree = recursive_func(Tree(permutation), n-1)
tree.addChild(child_tree)
get_permutations() gives a list of child trees to create. I also have a Tree class with a node value and a list of children.
Here is the Tree class:
class Tree:
def __init__(self, result):
self.node = node
self.children = []
def addChild(self, tree):
self.children.append(tree)
This is probably a rookie error in the design of my problem, but I would be glad to get some help.
TLDR: Since you use the result of recursive_func, it should always return its tree.
The recursive_func has three important points for this problem:
def recursive_func(tree, n):
if n == 0:
return tree # 1.
else:
permutations = get_permutations()
for permutation in permutations:
child_tree = recursive_func(Tree(permutation), n-1) # 2.
tree.addChild(child_tree)
# 3.
Now, 1. defines that the function sometimes returns a Tree. This matches 2. which always expects the function to return a Tree. However, both conflict with 3., which implicitly returns None when the second branch is done.
Since the first branch is empty aside from the return, both 1. and 3. can be collapsed to one path that always returns a Tree.
def recursive_func(tree, n):
if n > 0:
permutations = get_permutations()
for permutation in permutations:
child_tree = recursive_func(Tree(permutation), n-1)
tree.addChild(child_tree)
return tree

Generate all valid binary search trees given a list of values

Hello I am trying to solve the following question on leetcode, [https://leetcode.com/problems/unique-binary-search-trees-ii/].
I know I have access to the solution but I tried solving the problem my way and I am stuck and I would like to know if it is solvable the way I am doing it.
Here is the code:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def generateTrees(myrange, n, res = None):
if res == None:
res = []
if myrange == []:
res.append(None)
return
for root in myrange:
res.append(root)
generateTrees([i for i in range(root) if i in set(myrange)], n, res) #leftchild
generateTrees([i for i in range(root+1, n) if i in set(myrange)], n, res) #rightchild
return res
Initially myrange is just the list containing the node values, and n is the length of myrange.
The way I am doing it is a sort of DFS where I loop over the nodes making each one of them the root once and then I do the same for the left and right subtrees to get all combinations. But the problem I am facing is I can't figure out how to manage res to remove elements from it as my recursion backtracks (and make it so res only contains valid bst's and then put those in some other list that will be my actual result).
I would like some pointers or even just comments on if you think my approach is valid or bad ..etc.
Issues:
As you mention, your code only creates one list to which it keeps appending.
Even if you would fix that, the lists would never come out in the BFS kind of order, which is what the question's example seems to suggest.
For a chosen root, you need to list all combinations of its possible left subtrees with its possible right subtrees -- a Cartesian product if you wish. This logic is missing in your code.
I would:
not pass res as argument to the recursive function. Just return it, and let the caller deal with it.
not use ranges, as that only seems to complicate things. The if i in set(myrange) seems like an inefficient way to get the overlap between two ranges. I would instead pass the two extremes of the range as separate arguments.
use the TreeNode class to actually create the trees, and deal with generating the required output format later.
For generating the output format you need a BFS walk through the tree, and this could be implemented as a method on TreeNode.
Here is what I think would work:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def breadth_first(self):
lst = []
todo = [self]
while any(todo):
node = todo.pop(0)
lst.append(node.val if node else None)
if node:
todo.append(node.left)
todo.append(node.right)
return lst
def generateTrees(n):
def recur(start, end): # end is not included
if start >= end:
return [None]
trees = []
for root in range(start, end):
lefts = recur(start, root)
rights = recur(root+1, end)
# Cartesian product:
for left in lefts:
for right in rights:
# Start with a new tree, and append to result
tree = TreeNode(root)
tree.left = left
tree.right = right
trees.append(tree)
return trees
return recur(1, n+1)
# Create the trees as a list of TreeNode instances:
trees = generateTrees(3)
# Convert to a list of lists
print([tree.breadth_first() for tree in trees])

Breadth First Tree Traversal using Generators in Python

I am studying how to use Generators in Python in David Beazly's excellent Python Cookbook text. The following code recipe defines Depth First Tree Traversal using generators very elegantly:
# example.py
#
# Example of depth-first search using a generator
class Node:
def __init__(self, value):
self._value = value
self._children = []
def __repr__(self):
return 'Node({!r})'.format(self._value)
def add_child(self, node):
self._children.append(node)
def __iter__(self):
return iter(self._children)
def depth_first(self):
yield self
for c in self:
yield from c.depth_first()
# Example
if __name__ == '__main__':
root = Node(0)
child1 = Node(1)
child2 = Node(2)
root.add_child(child1)
root.add_child(child2)
child1.add_child(Node(3))
child1.add_child(Node(4))
child2.add_child(Node(5))
for ch in root.depth_first():
print(ch)
# Outputs: Node(0), Node(1), Node(3), Node(4), Node(2), Node(5)
I am trying to come up with an equally elegant method
def breadth_first(self):
pass
I am deliberately not posting the crazy stuff that I have been trying since everything that I have tried requires maintaining 'state' within it. I don't want to use the traditional queue based solutions. The whole point of this academic exercise is to learn how generators behave in depth. Therefore, I want to create a parallel 'breadth_first' method using generators for the tree above.
Any pointers/solutions are welcome.
You can't use recursion (stack) for bfs without some serious hacks, but a queue would work:
def breadth_first(self):
q = [self]
while q:
n = q.pop(0)
yield n
for c in n._children:
q.append(c)
I find it both useful and elegant generating the whole breadth, one level at a time. Python 3 generator below:
def get_level(t: Node) -> Iterable[List]:
curr_level = [t] if t else []
while len(curr_level) > 0:
yield [node._value for node in curr_level]
curr_level = [child for parent in curr_level
for child in parent._children
if child]
It would be easy if itertools had:
# zip_chain('ABCD', 'xy') --> A x B y C D
It's almost a itertools.chain(itertools.zip_longest()), but not quite.
Anyway, zip_chain allows:
def bf(self):
yield self
yield from zip_chain(*map(Node.bf, self.children))
It doesn't create a whole row at a time either, I think,
The depth first solution you implement is essentially "iterate then recurse"
def depth_first(self):
yield self
for c in self.children:
yield from c.depth_first():
Inspired by this blog post the activestate post it references, you obtain a breadth first search as "recurse then iterate":
def breadth_first(self):
yield self
for c in self.breadth_first():
if not c.children:
return # stop the recursion as soon as we hit a leaf
yield from c.children
Edit: it turns out that the linked blog says "Termination check is absent", replaced with an activestate link, which I've adapted to use yield from above.

Resources