How to increment height of each path in a suffix trie - python

I have created a suffix trie and I am trying to find a way to accumulate and store its height in the reverse order and store it in each node. Example:
with strings ['abcd', 'abc', 'aa']
So I have implemented this in two classes. 1 is the Node class and 2 is the Trie class, and each vertex is a node, and hence to represent each of the height would be to go to each vertex and retrieve
ex. a.height = 4, b.height = 3, c.height = 2 instead of the normal (easy) a.height = 1, b.height = 2, c.height = 3
The nodes doesn't have to be the same height away from the root node and the $ sigh represents the end of a string. The height is added up from the bottom ($) as indicated from the image
I have been able to store the frequency of how often each character gets repeated in the Trie by simply initializing in the class Trienode -> self.freq = 1 and updating it during insert. But this is not the height and I'm out of ideas. Any suggestions would be welcome.
Here's my code without the frequency update:
class TrieNode:
# Trie node class
def __init__(self):
self.children = [None]*26
# isEndOfWord is True if node represent the end of the word
self.isEndOfWord = False
class Trie:
# Trie data structure class
def __init__(self):
self.root = self.getNode()
def getNode(self):
# Returns new trie node (initialized to NULLs)
return TrieNode()
def _charToIndex(self,ch):
# private helper function
# Converts key current character into index
# use only 'a' through 'z' and lower case
return ord(ch)-ord('a')
def insert(self,key):
# If not present, inserts key into trie
# If the key is prefix of trie node,
# just marks leaf node
pCrawl = self.root
length = len(key)
for level in range(length):
index = self._charToIndex(key[level])
# if current character is not present
if not pCrawl.children[index]:
pCrawl.children[index] = self.getNode()
pCrawl = pCrawl.children[index]
# mark last node as leaf
pCrawl.isEndOfWord = True
Thanks

Doing this recursively is probably the best way. You'll need to update your TrieNode class to include a 'height' attribute for this.
def compute_heights(self, node=None):
if not node: node = self.root # Default value
# Base case: If we have found an end, return 0 as the height.
if node.isEndOfWord:
return 0
max_so_far = 0 # Use this to track the maximum height among all children
# Iterate over all children
for child in node.children:
self.compute_heights(child) # Recursively compute height
max_so_far = max(max_so_far, child.height)
node.height = 1 + max_so_far # Update the current node's height
You can simply call this function on an instance, the node value is set to the root if none is provided. The function can also be modified to return the height of the node it is called on.
You can run this after completely defining a trie. To allow insertions and update these values live, you will need to search upwards from the newly-created node for which you will probably need to keep track of each node's parent. With that, you can iteratively check at each step on the way from the inserted leaf to the root if the height needs to be updated.

Related

What is wrong with this iterative solution in validating Binary Search Tree?

The goal is, given the root of a binary tree, determine if it is a valid binary search tree (BST).
A valid BST is defined as follows:
The left subtree of a node contains only nodes with keys less than the node's key.
The right subtree of a node contains only nodes with keys greater than the node's key.
Both the left and right subtrees must also be binary search trees.
The problem setting is the same as here
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
level=[root]
while level:
for k in level:
if k.left:
if k.left >= k.val:
return False
if k.right:
if k.right <= k.val:
return False
level=[leaf for n in level for leaf in (n.left,n.right) if leaf]
return True
The above solution could not work for the case [2,1,3] which I believe the bug is located at this part after some debugging processes
if k.left:
if k.left >= k.val:
return False
What is wrong with it? Thanks in advance for any help.
There are a couple of issues with your code:
In the lines if k.left >= k.val: and if k.right <= k.val: you are comparing nodes to values, which I assume is not your intention.
Assuming the above is corrected, the general logic only compares the values of children with those of their immediate parents, which is not enough to satisfy the part of the problem specification stating, "The left subtree of a node contains only nodes with keys less than the node's key", or the symmetrical part of the spec regarding the right subtree. To satisfy this, you need to track the ancestral bounds with which a given node's val attribute must comply.
Here's an example of a modification to your code that would work as an iterative solution:
# Definition for a binary tree node.
class TreeNode(object):
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution(object):
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
level=[(root,None,None)]
while level:
level2 = []
for k,floor,ceil in level:
if k.left:
if k.left.val >= k.val or floor is not None and k.left.val <= floor:
return False
level2.append((k.left,floor,k.val))
if k.right:
if k.right.val <= k.val or ceil is not None and k.right.val >= ceil:
return False
level2.append((k.right,k.val,ceil))
level = level2
return True
t = TreeNode(2, TreeNode(1), TreeNode(3))
x = Solution()
print(x.isValidBST(t))
Ouput:
True
Explanation:
instead of storing each non-null node at a given level, we store a tuple for each such node including the node itself as well as floor and ceil values (set to None for root) that will allow us to carry forward (or downward as we descend the levels of the tree) the bounds to which a given subtree must conform.
When we traverse leftward via a non-null left attribute, we overwrite ceil with the immediate parent's val attribute, and for rightward traversal via a non-null right attribute we overwrite floor.

Sort a linked list, handling pairs as a single item

I am working on this challenge:
Sort a Linked list in increasing order by considering two nodes as a two digit number. (in place sorting)
4 ≤ n ≤ 1000
The length of the linked list is always even.
Example 1:
Input
1→3→4→2→1→2
Output
1→2→1→3→4→2
Explanation:
12 > 13 > 42
Example 2:
Input
1→3→0→3
Output
0→3→1→3
Here is my linked list template implementation; which anyone can start coding:
class Node():
def __init__(self,val):
self.data = val
self.next = None
class Linkedlist():
def __init__(self):
self.head = None
def add(self,x):
t = Node(x)
t.next = self.head
self.head = t
def display(self):
p = self.head
while p != None:
print(p.data)
p=p.next
def sort(self):
curr = self.head
# your logic here
m = Linkedlist()
m.add(1)
m.add(3)
m.add(4)
m.add(2)
m.add(1)
m.add(2)
m.display()
Which is the algorithm to sort the pairs in a linked list (in place), and how can I code it?
First write a function that accepts a linked list of single digit nodes and merges each pair of adjacent single digit nodes into a linked list of half as many nodes, where each node contains a double digit number.
Then sort the resulting linked list using bubble sort, which is O(n^2), or mergesort, which is O(nlogn).
Then, if need be, write a third function that takes apart the double digit nodes and creates a new list of twice as many single digit nodes.
Doing it with two or three functions like this will really simplify the sort.
First of all, your current code will not generate list 1→3→4→2→1→2, but its inverse. This is because your definition of the add method will prepend the given value to the list.
To make add work correctly, it should first find the last node in the list, and append the new node after it. As this is not a very efficient method to add many values to a list, I would suggest to also define add as a Node method, and to allow chaining add calls. That way you can add many values in one chained expression without being inefficient.
Also, as you will need to compare values of pairs, it makes sense to make a method on Node that will return the value of the pair formed by that node and the next in the list.
Finally, for the sort algorithm itself, you could reduce the list to its first pair only (which is then obviously sorted), and treat the rest as a separate list of unsorted nodes. Then extract a pair at a time from the unsorted list, and find the right place to inject it in the main list, so that it remains sorted.
This process represents O(n²) time complexity on average and in the worst case. The best case is O(n), which occurs when the list is originally sorted in reverse order.
Here is how that could look:
class Node():
def __init__(self, val):
self.data = val
self.next = None
# Easy chaining: defining this method on a Node as well
def add(self, val):
node = Node(val)
node.next = self.next
self.next = node
return node
# Define how the value of a pair is calculated
def pairvalue(self):
return self.data * 10 + self.next.data
class Linkedlist():
def __init__(self):
self.head = None
def add(self, val):
# Corrected method: addition should be at the end of the list
node = Node(val)
if not self.head:
self.head = node
else: # Look for the last node in the list
curr = self.head
while curr.next:
curr = curr.next
# ...and append the new node after it
curr.next = node
return node # Allow for chaining
def display(self):
p = self.head
while p:
print(p.data, end= "→")
p = p.next
print("NIL")
def sort(self):
if not self.head:
return
# Reduce list to just its first pair
# The remainder is a temporary "todo" list
todo = self.head.next.next
self.head.next.next = None
while todo:
pair = todo # The pair that will be added to the sorted list
todo = todo.next.next
val = pair.pairvalue()
if val < self.head.pairvalue():
# The pair is a minimum: prepend it
pair.next.next = self.head
self.head = pair
else:
curr = self.head.next # odd index
# Find insertion point in sorted list
while curr.next and curr.next.pairvalue() < val:
curr = curr.next.next
# Perform insertion
pair.next.next = curr.next
curr.next = pair
m = Linkedlist()
m.add(1).add(3).add(4).add(2).add(1).add(2)
m.display() # original list
m.sort()
m.display() # final list
The output:
1→3→4→2→1→2→NIL
1→2→1→3→4→2→NIL

How to generate a Singly Linked List from a number's digits?

I have defined a class to make a linked list as follows:
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
Now, I've generated a number "sum" and wrote the following code to generate the Linked List: (assuming that sum could possibly have 3 or 4 digits)
if len(str(sum)) == 4:
l3 = ListNode(str(sum)[3])
l3.next = ListNode(str(sum)[2])
l3.next.next = ListNode(str(sum)[1])
l3.next.next.next = ListNode(str(sum)[0])
elif len(str(sum)) == 3:
l3 = ListNode(str(sum)[2])
l3.next = ListNode(str(sum)[1])
l3.next.next = ListNode(str(sum)[0])
Is there a way to generate the above using the length of the number "sum" without hard-coding as above?
you could do something like this:
s = str(975)
start_node = node = ListNode(s[-1]) # initialize to last character in s
for c in reversed(s[:-1]):
node.next = ListNode(c)
node = node.next
start_node should now contain the first node; from there you could .next to the next node. node always points to the current node in the loop (and could be discarded after the loop).
note that sum is a built-in function and therefore not a good variable name...
the output of the code above:
print(start_node.val) # 5
print(start_node.next.val) # 7
print(start_node.next.next.val) # 9
print(start_node.next.next.next) # None
you could even write the code above more compact (but way less readable imo):
start_node = node = ListNode(s[-1])
for c in reversed(s[:-1]):
node.next = node = ListNode(c)

Pathfinding code produces unexpected results

First of all, excuse the bad title, but I don't know how to describe this in just one sentence...
Given a grid with 3 kinds of fields, empty fields, walls and exits, I wrote a program that checks for every empty field, whether that field is "safe".
A person walks through that grid, but can only walk non-diagonally and can't go through walls. The person, starting at one field, chooses one direction at random and starts walking that way. Once it hits a wall, it chooses a direction at random again, starting to move into that direction and so on.
A field is considered safe if a person traversing the grid as described above, starting at that field, is guaranteed to find an exit at some point.
I wrote a Python program to solve this problem. It builds a "tree" for every field it checks, containing every possible route from that field.
I have a function that just returns the "parent" of a given node, by recursively adding the parent of the current node to a list of nodes until it reaches the topmost node.
The program works as expected when checking only one field, for example (1, 4). However it doesn't work when checking all fields of the example grid.
I already looked into it and realized that the alle_parents() function which returns all parents of a given node yields unexpected results when checking all nodes. E.g. when checking the field (1, 4), one child of that node is (1, 8). The parents of (1, 8) should just be (1, 4). That's not the case, though. alle_parents((1, 8)) returns many different fields that shouldn't be there. However I can't figure out why it behaves as it does. My only guess is that it has to do with "left-over" data/GC not working as intended.
Relevant code:
class Knoten():
def __init__(self, x, y, parent = None):
self.x = x
self.y = y
self.parent = parent
self.children = []
n = len(spielfeld)
m = len(spielfeld[0])
for k in range(n):
for j in range(m):
if spielfeld[k][j] not in [None, '#', 'E']:
baum = []
i = 0
ebene = []
ebene.append(Knoten(k, j))
baum.append(ebene)
i += 1
while i <= 100:
ebene = []
for knoten in baum[i - 1]:
children = []
if spielfeld[knoten.x][knoten.y] == 'E':
continue
for feld in next_feld(knoten.x, knoten.y):
knoten_neu = Knoten(feld[0], feld[1], knoten)
hinzufuegen = True
for parent in alle_parents(knoten_neu):
if knoten_neu.x == parent.x and knoten_neu.y == parent.y:
hinzufuegen = False
if hinzufuegen:
ebene.append(knoten_neu)
children.append(knoten_neu)
knoten.children = children
if children == []:
if spielfeld[knoten.x][knoten.y] != 'E':
spielfeld[k][j] = '%' # Field not safe
baum.append(ebene)
i += 1
def alle_parents(knoten, parents = []):
if knoten.parent == None:
return parents
else:
parents.append(knoten.parent)
return alle_parents(knoten.parent, parents)
The example map I'm using:
############
# # # #
# ## #
# # E# #
# ## #
# #
# #E E###
############
Full code (parts of it are German, sorry for that): http://pastebin.com/3XUBbpkK
I suspect your issue is a common Python gotcha. This line:
def alle_parents(knoten, parents = []):
Creates an empty array when the module is loaded, NOT every time the function is called. Future calls to alle_parents() will reuse the same array (which may have grown in size) instead of a new empty array! A good way to fix is to do this:
def alle_parents(knoten, parents = None):
parents = parents or []

Traversing a non-binary tree

I've made custom class for nodes
class NodeTree(object):
def __init__(self, name = None, children = None):
self.name = name
self.children = children
and defined a function that make a tree(a node containing its children nodes)
def create_tree(d):
x = NodeTree()
for a in d.keys():
if type(d[a]) == str:
x.name = d[a]
if type(d[a]) == list:
if d[a] != []:
for b in d[a]:
x.add_child(create_tree(b))
return x
The input is a dict with one argument for the node name and a list with its children in the same form as the parent.
The function work fine and I've made method that prove it but I can't find a way to traverse it right and get the height of the tree. I don't know if "height" it's the right term cause I know it may be ambivalent, I need to count the node as a measure unit, like this:
parent
|
|
---------
| |
child child
The height of this tree is 2, I've tried everything, from counters to tag in the class, everything seems to degenerate an I never get the right height.
How should I approach that?
To create a recursive height method for your tree that determines the height of the node (that is, the maximum number of nodes in a path from that node to a leaf):
def height(self):
if not self.children: # base case
return 1
else: # recursive case
return 1 + max(child.height() for child in self.children)
Other tree traversals can also be done recursively. For example, here's a generator method that yields the names of the trees nodes in "pre-order" (that is, with each parent preceding its children and decedents):
def preorder(self):
yield self.name
for child in self.children:
yield from child.preorder() # Python 3.3 only!
The yield from syntax in that loop is new in Python 3.3. You can get the same results in earlier versions with this:
for descendent in child.preorder():
yield descendent

Categories