Python - Flat list tree implementation: Given child, get parent? - python

I'm creating a python class for a tree in which each node has a number of children given by "order" (but each child only has one node). I have a method, children(self,i), which returns the children of a node at index i. I need to implement parent(self, i) which will get the parent of a child at index i.
Here's what I have so far:
class Tree:
def __init__(self, order=2, l=[]):
self._tree = l
self._order = order
def children(self, i):
left = self._tree[(i+1)*self._order-1]
right = self._tree[(i+1)*self._order]
return [left, right]
def parent(self, i):
if i>len(self._tree):
return ValueError
elif i==0:
return None
else:
#get parent of node i
An example tree represented by order=2 and list [45, 2, 123, 1, 8, 40, 456] would look like this:
45
/ \
2 123
/ \ / \
1 8 40 456
I know that there's probably a way I can reverse the method I used for children(self, i) but I'm not sure how.

You would do the inverse operation:
else:
#get parent of node i
return self._tree[(i-1)//self._order]
Note that your implementation is only working for binary trees (you return two children, not n). Correct it like this:
def children(self, i):
return self._tree[(i*self._order+1):((i+1)*self._order+1)]

Related

Leetcode Problem 1038. Binary Search Tree to Greater Sum Tree --> Python

I am doing the above leetcode problem in Python. Typically what I do is I solve the problem in a jupyter notebook and then copy and paste it into the leetcode solution box once I am done with it. I am having issues with this problem, however.
The problem definition is defined below:
Given the root of a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus sum of all keys greater than the original key in BST.
As a reminder, a binary search tree is a tree that satisfies these constraints:
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.
A sample input and output for the problem is shown below
Input: root = [4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
Output: [30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
Furthermore the problem solution is set up as follows
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def bstToGst(self, root: TreeNode) -> TreeNode:
I am confused as to how to approach this problem. Initially I thought I would do some sort of looping through the list provided. Upon reading some sample responses from the discussion, however, I see that commands such as root.right and root.left are used. How do I go about doing this in a jupyter notebook? I have no expereince with TreeNodes so I want to this the problem the right way and learn the fundamental concept instead of brute forcing through it another way. All help is greatly appreciated.
Thanks
Define an iterator function to traverse the nodes in reverse order, Then accumulate the total going backwards through the nodes and assign the values to each node:
class Solution:
def revNodes(self,node):
if node.right: yield from self.revNodes(node.right)
yield node
if node.left: yield from self.revNodes(node.left)
def bstToGst(self, root):
total = 0
for node in self.revNodes(root):
node.val = total = node.val + total
output:
data = [4,1,6,0,2,5,7,None,None,None,3,None,None,None,8]
nodes = [v if v is None else TreeNode(v) for v in data]
for i,node in enumerate(nodes):
if not node: continue
if 2*i+1<len(nodes): node.left = nodes[2*i+1]
if 2*i+2<len(nodes): node.right = nodes[2*i+2]
root = nodes[0]
print(root) # BEFORE
4
__/ \_
1 6
/ \ / \
0 2 5 7
\ \
3 8
Solution().bstToGst(root)
print([node.val if node else None for node in nodes])
[30, 36, 21, 36, 35, 26, 15, None, None, None, 33, None, None, None, 8]
print(root) # AFTER
30
__/ \__
36 21
/ \ / \
36 35 26 15
\ \
33 8
Note that, in order to print the tree, I had to add a repr() method to the TreeNode class
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def __repr__(self):
nodeInfo = lambda n:(str(n.val),n.left,n.right)
return "\n".join(printBTree(self,nodeInfo,isTop=False))
The printBTree function is from another answer I provided in the past here

Sum of nodes in a subtree (not binary)

I'm currently trying to find the sum of all nodes in a specified subtree. For example if I have a tree
A(5)
/ \
B(5) C(6)
/ / \
D(3) E(3) F(7)
|
G(1)
and I want to know the the sum(C), which should return 17.
This is the code I came up with using recursion, but I can't seem to reach a subtree which has more than 2 levels. E.g. my algorithm doesn't seem to reach G. I'm trying to get better at recursion, but I can't seem to fix this.
def navigate_tree(node,key): #node of the root of subtree, along with its key
children = node.get_children()
if (len(children) ==0):
return node.key
else:
for child in children: #not a binary tree so trying to loop through siblings
key += navigate_tree(child,key) #summing up key recursively
return key
You would be better with an improved interface and being able to lean on the features of collections:
def navigate_tree(node):
children = node.get_children()
key = node.key
for child in children:
key += navigate_tree(child)
return key
# class Node and data A..G elided
print(navigate_tree(C))
Output:
17
The reason why your code appeared not to work, was that you were passing the previous key down to the next level of recursion. However, your code seemed to recurse OK. If you had added some print(node.key) you would have seen that you were visiting all the correct nodes.
You can use recursion with sum:
class Node:
def __init__(self, n, v, c=[]):
self.name, self.val, self.children = n, v, c
def get_sum(node):
return node.val+sum(map(get_sum, node.children))
tree = Node('A', 5, [Node('B', 5, [Node('D', 3)]), Node('C', 6, [Node('E', 3), Node('F', 7, [Node('G', 1)])])])
print(get_sum(tree.children[-1]))
Output:
17
However, if you do not have access to the exact node C, you can apply a simple search as part of the recursive function:
def get_sum(t, node):
def inner_sum(d, s=False):
return d.val*(s or d.name == t)+sum(inner_sum(i, s or d.name == t) for i in d.children)
return inner_sum(node)
print(get_sum('C', tree))
Output:
17

Find minimum depth of binary tree solution does not work when one side of tree is null

I am trying to understand why my solution to find the minimum depth of binary tree does not work when one side of the tree is None.
There's already a question asked on this here - Why won't my solution work to find minimum depth of a binary tree? but the answer there still does not make me understand.
My implementation code is as below.
class Solution:
def minDepth(self, root: 'TreeNode') -> 'int':
if root is None:
return 0
left = self.minDepth(root.left)
right = self.minDepth(root.right)
min_depth = min(left, right)
return 1 + min_depth
When the last line is modified to the following, it works.
if left == 0 or right == 0:
return 1 + left + right
return 1 + min_depth
My question is, why do you need to check if one side is None or not if in the end, you sum them all up anyway - return 1 + left + right?
Test case is
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
# [1, 2]
root = TreeNode(1)
root.left = TreeNode(2)
solution = Solution()
solution.minDepth(root)
My code returns 1 where it should return 2.
EDIT
The depth of the tree is defined as the number of nodes in the shortest path from the root of the tree to a leaf node.
When you are at a node that has only one child, then in your first code version, the min_depth for that node will be 0 (since one of the recursive calls will return 0).
That is indeed wrong, because the node is not a leaf. It would only be correct if the node were a leaf (without children).
In your example, the root is such a node (with one child). This is what happens:
minDepth(root) is called
....minDepth(root.left) is called
........minDepth(root.left.left) is called and returns 0, because it isNone`
........minDepth(root.left.right) is called and returns 0, because it isNone`
........min_depth = min(left, right) evaluates to 0
........the return value is 1 for minDepth(root.left)
....minDepth(root.right) is called and returns 0, because it is None
....min_depth = min(left, right) evaluates to 0, which is wrong.
....the final return value is thus 1 (wrong).
When you are in the situation where either left or right is 0, you need to get the minDepth of the remaining child and add 1 to it. That is why it works when you add this:
if left == 0 or right == 0:
return 1 + left + right

How to implement 'range' with a python BST

My code at the moment allows me to search for a specific node, I would like to edit it so that I can search within a range of numbers. For example, I have a price list of apples, and I would like to add all apples to a list/dictionary that cost between $2-$4 or something like that.
Here is my current code
def valueOf(self, key):
node = self._bstSearch(self._root, key)
assert node is not None, "Invalid map key."
return node.value
def _bstSearch(self, subtree, target):
if subtree is None:
return None
elif target < subtree.key:
return self._bstSearch( subtree.left, target)
elif target > subtree.key:
return self._bstSearch(subtree.right, target)
else:
return subtree
I think I should be editing target to change it from a single item search to a ranged item search but I'm not 100% sure how to
Using recursive in-order traversal:
def range(self, a, b):
return self._traverse_range(self._root, a, b)
def _traverse_range(self, subtree, a, b, cumresult=None):
if subtree is None:
return
# Cumulative variable.
if cumresult is None:
cumresult = []
# Traverse LEFT subtree if it is possible to find values in required range there.
if subtree.key > a:
self._traverse_range(subtree.left, a, b, cumresult)
# Push VALUE if it is in our range.
if a <= subtree.key <= b: # Change to strict "< b" to act like python's range
cumresult.append(subtree.key)
# Traverse RIGHT subtree if it is possible to find values in required range there.
if subtree.key < b:
self._traverse_range(subtree.right, a, b, cumresult)
return cumresult

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