Maximum Sum Path in a Binary Tree - python

I am trying to solve this question: https://leetcode.com/problems/binary-tree-maximum-path-sum/description/.
To find maximum sum path is like finding maximum path between any two nodes, that path may or may not pass through the root; except that with max sum path we want to track sum instead of path length.
So, I adapted the diameter of a binary tree's solution to find the maximum sum path.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def height(self, root):
if root==None:
return 0
lh = self.height(root.left)
rh = self.height(root.right)
return max(root.val+lh, root.val+rh)
def maxPathSum(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if root==None:
return 0
lh = self.height(root.left)
rh = self.height(root.right)
ld = self.maxPathSum(root.left)
rd = self.maxPathSum(root.right)
return max(lh+rh+root.val, root.val+ max(ld , rd))
My solution is passing 40 test cases before failing.
I am stuck at trying to figure out the correct solution. My solution works for finding the diameter, I just added sum in the return values.
Why is this not a generic solution as i am clearly traversing all the paths and taking appropriate max in return values.
Thanks for your help.

The problem is this line
return max(lh+rh+root.val, root.val+ max(ld , rd))
It should be
return max(lh+rh+root.val, max(ld , rd))
Note that the root node is not part of the path for ld and rd.

Related

Count Number of Good Nodes

problem statement
I am having trouble understanding what is wrong with my code and understanding the constraint below.
My pseudocode:
Traverse the tree Level Order and construct the array representation (input is actually given as a single root, but they use array representation to show the full tree)
iterate over this array representation, skipping null nodes
for each node, let's call it X, iterate upwards until we reach the root checking to see if at any point in the path, parentNode > nodeX, meaning, nodeX is not a good node.
increment counter if the node is good
Constraints:
The number of nodes in the binary tree is in the range [1, 10^5].
Each node's value is between [-10^4, 10^4]
First of all:
My confusion on the constraint is that, the automated tests are giving input such as [2,4,4,4,null,1,3,null,null,5,null,null,null,null,5,4,4] and if we follow the rules that childs are at c1 = 2k+1 and c2 = 2k+2 and parent = (k-1)//2 then this means that there are nodes with value null
Secondly:
For the input above, my code outputs 8, the expected value is 6, but when I draw the tree from the array, I also think the answer should be 8!
tree of input
# 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 goodNodes(self, root: TreeNode) -> int:
arrRepresentation = []
queue = []
queue.append(root)
# while queue not empty
while queue:
# remove node
node = queue.pop(0)
if node is None:
arrRepresentation.append(None)
else:
arrRepresentation.append(node.val)
if node is not None:
# add left to queue
queue.append(node.left)
# add right to queue
queue.append(node.right)
print(arrRepresentation)
goodNodeCounter = 1
# iterate over array representation of binary tree
for k in range(len(arrRepresentation)-1, 0, -1):
child = arrRepresentation[k]
if child is None:
continue
isGoodNode = self._isGoodNode(k, arrRepresentation)
print('is good: ' + str(isGoodNode))
if isGoodNode:
goodNodeCounter += 1
return goodNodeCounter
def _isGoodNode(self, k, arrRepresentation):
child = arrRepresentation[k]
print('child: '+str(child))
# calculate index of parent
parentIndex = (k-1)//2
isGood = True
# if we have not reached root node
while parentIndex >= 0:
parent = arrRepresentation[parentIndex]
print('parent: '+ str(parent))
# calculate index of parent
parentIndex = (parentIndex-1)//2
if parent is None:
continue
if parent > child:
isGood = False
break
return isGood
Recursion might be easier:
class Node:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def good_nodes(root, maximum=float('-inf')):
if not root: # null-root
return 0
is_this_good = maximum <= root.val # is this root a good node?
maximum = max(maximum, root.val) # update max
good_from_left = good_nodes(root.left, maximum) if root.left else 0
good_from_right = good_nodes(root.right, maximum) if root.right else 0
return is_this_good + good_from_left + good_from_right
tree = Node(2, Node(4, Node(4)), Node(4, Node(1, Node(5, None, Node(5, Node(4), Node(4)))), Node(3)))
print(good_nodes(tree)) # 6
Basically, recursion traverses the tree while updating the maximum number seen so far. At each iteration, the value of a root is compared with the maximum, incrementing the counter if necessary.
Since you wanted to solve with breadth first search:
from collections import deque
class Solution:
def goodNodes(self,root:TreeNode)->int:
if not root:
return 0
queue=deque()
# run bfs with track of max_val till its parent node
queue.append((root,-inf))
res=0
while queue:
current,max_val=queue.popleft()
if current.val>=max_val:
res+=1
if current.left:
queue.append((current.left,max(max_val,current.val)))
if current.right:
queue.append((current.right,max(max_val,current.val)))
return res
I added the node and its max_value till its parent node. I could not add a global max_value, because look at this tree:
For the first 3 nodes, you would have this [3,1,4] and if you were keeping the max_val globally, max_val would be 4.
Now next node would be 3, leaf node on the left. Since max_node is 4, 3<4 would be incorrect so 3 would not be considered as good node. So instead, I keep track of max_val of each node till its parent node
The binary heap you provided corresponds to the folloring hierarchy:
tree = [2,4,4,4,None,1,3,None,None,5,None,None,None,None,5,4,4]
printHeapTree(tree)
2
/ \
4 4
/ / \
4 1 3
\
5
In that tree, only item value 1 has an ancestor that is greater than itself. The 6 other nodes are good, because they have no ancestor that are greater than themselves (counting the root as good).
Note that there are values in the list that are unreachable because their parent is null (None) so they are not part of the tree (this could be a copy/paste mistake though). If we replace these None values by something else to make them part of the tree, we can see where the unreachable nodes are located in the hierarchy:
t = [2,4,4,4,'*', 1,3,'*',None, 5,None, None,None,None,5,4,4]
printHeapTree(t)
2
__/ \_
4 4
/ \ / \
4 * 1 3
/ / \
* 5 5
/ \
4 4
This is likely where the difference between a result of 8 (not counting root as good) vs 6 (counting root as good) comes from.
You can find the printHeapTree() function here.

Diameter of binary tree fails 4 out of 104 test cases

I am working on Leet Code problem 543. Diameter of Binary Tree:
Given the root of a binary tree, return the length of the diameter of the tree.
The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.
The length of a path between two nodes is represented by the number of edges between them.
Example 1
Input: root = [1,2,3,4,5]
Output: 3
Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3].
This is my attempt:
def diameterOfBinaryTree(self, root):
return self.getHeight(root.left) + self.getHeight(root.right)
def getHeight(self, root):
if not root:
return 0
return max(self.getHeight(root.left), self.getHeight(root.right)) + 1
I got 100/104 test cases passed.
The test case that I got wrong had an input of [4,-7,-3,null,null,-9,-3,9,-7,-4,null,6,null,-6,-6,null,null,0,6,5,null,9,null,null,-1,-4,null,null,null,-2] with an expected result of 8. However, due to the logics of my solution, I got 7 instead and have no idea how I could be wrong.
The only test case that I got wrong had an input of [4,-7,-3,null,null,-9,-3,9,-7,-4,null,6,null,-6,-6,null,null,0,6,5,null,9,null,null,-1,-4,null,null,null,-2] ... have no idea how I could be wrong.
The provided tree looks like this:
___4___
/ \
-7 ___-3_____
/ \
___-9___ -3
/ \ /
9 -7 -4
/ / \
__6__ -6 -6
/ \ / /
0 6 5 9
\ / /
-1 -4 -2
The longest path is indeed 8, because the longest path in the subtree rooted at -9 has that longest path. Your code does not find that longest path because it requires that the root is part of it.
So, you should check what the longest path is for any subtree (recursively) and retain the longest among these options:
The longest path found in the left subtree
The longest path found in the right subtree
The longest path that can be made by including the root (i.e. left-height + right-height + 1)
Your code does not take the two first options into account and always goes for the third.
The above is implemented in this (hidden) solution. First try it yourself:
class Solution(object):
def diameterOfBinaryTree(self, root):
self.longestPath = 0
def levels(node): # With side-effect: it updates longestPath
if not node:
return 0
leftLevels = levels(node.left)
rightLevels = levels(node.right)
self.longestPath = max(self.longestPath, leftLevels + rightLevels)
return 1 + max(leftLevels, rightLevels)
levels(root) # Not interested in the returned value...
return self.longestPath # ...but all the more in the side effect!
You code assumes that diameter of the binary tree will always go through the root, which is not the case. You have to consider the subtrees with longer diameters. One way to do it can be found below, it is a slightly modified version of your code:
class Solution(object):
maximum = 0
def getHeight(self, root):
if not root:
return 0
left = self.getHeight(root.left)
right = self.getHeight(root.right)
sub_diameter = left + right
if(self.maximum < sub_diameter): self.maximum = sub_diameter
return max(left, right) + 1
def diameterOfBinaryTree(self, root):
return max(self.getHeight(root.left) + self.getHeight(root.right), self.maximum)
Tested, it passes all testcases.
Main logic is to keep the maximum diameter value for subtrees and compare it with the diameter that goes through the root at the end.

Bad Tree design, Data Structure

I tried making a Tree as a part of my Data Structures course. The code works but is extremely slow, almost double the time that is accepted for the course. I do not have experience with Data Structures and Algorithms but I need to optimize the program. If anyone has any tips, advices, criticism I would greatly appreciate it.
The tree is not necessarily a binary tree.
Here is the code:
import sys
import threading
class Node:
def __init__(self,value):
self.value = value
self.children = []
self.parent = None
def add_child(self,child):
child.parent = self
self.children.append(child)
def compute_height(n, parents):
found = False
indices = []
for i in range(n):
indices.append(i)
for i in range(len(parents)):
currentItem = parents[i]
if currentItem == -1:
root = Node(parents[i])
startingIndex = i
found = True
break
if found == False:
root = Node(parents[0])
startingIndex = 0
return recursion(startingIndex,root,indices,parents)
def recursion(index,toWhomAdd,indexes,values):
children = []
for i in range(len(values)):
if index == values[i]:
children.append(indexes[i])
newNode = Node(indexes[i])
toWhomAdd.add_child(newNode)
recursion(i, newNode, indexes, values)
return toWhomAdd
def checkHeight(node):
if node == '' or node == None or node == []:
return 0
counter = []
for i in node.children:
counter.append(checkHeight(i))
if node.children != []:
mostChildren = max(counter)
else:
mostChildren = 0
return(1 + mostChildren)
def main():
n = int(int(input()))
parents = list(map(int, input().split()))
root = compute_height(n, parents)
print(checkHeight(root))
sys.setrecursionlimit(10**7) # max depth of recursion
threading.stack_size(2**27) # new thread will get stack of such size
threading.Thread(target=main).start()
Edit:
For this input(first number being number of nodes and other numbers the node's values)
5
4 -1 4 1 1
We expect this output(height of the tree)
3
Another example:
Input:
5
-1 0 4 0 3
Output:
4
It looks like the value that is given for a node, is a reference by index of another node (its parent). This is nowhere stated in the question, but if that assumption is right, you don't really need to create the tree with Node instances. Just read the input into a list (which you already do), and you actually have the tree encoded in it.
So for example, the list [4, -1, 4, 1, 1] represents this tree, where the labels are the indices in this list:
1
/ \
4 3
/ \
0 2
The height of this tree — according to the definition given in Wikipedia — would be 2. But apparently the expected result is 3, which is the number of nodes (not edges) on the longest path from the root to a leaf, or — otherwise put — the number of levels in the tree.
The idea to use recursion is correct, but you can do it bottom up (starting at any node), getting the result of the parent recursively, and adding one to 1. Use the principle of dynamic programming by storing the result for each node in a separate list, which I called levels:
def get_num_levels(parents):
levels = [0] * len(parents)
def recur(node):
if levels[node] == 0: # this node's level hasn't been determined yet
parent = parents[node]
levels[node] = 1 if parent == -1 else recur(parent) + 1
return levels[node]
for node in range(len(parents)):
recur(node)
return max(levels)
And the main code could be as you had it:
def main():
n = int(int(input()))
parents = list(map(int, input().split()))
print(get_num_levels(parents))

Is there a bug in my custom tree implementation for finding shortest path to leaf node?

I'm trying to implement a custom tree, for a problem that takes the input as the number of nodes and the edges/connections between each node and gives the output as the length of the shortest path to the leaf node from the root.
For the smaller test cases, I've tried it gave correct output but for some reason, it gives runtime error on hackerank for all the other cases, is there a bug? I tried to find it but I coudnt
Code:
class tree:
class Node:
def __init__(self,parent,children=[]):
self.Parent=parent # p capital
self.children=children
def __init__(self,n,connections=[]):
self.root = self.Node(None,[])
self.n = n # tree.n
self.connections = connections # list of all edges
self.Positions = {} # initializing all positions in dictionary , for all nodes
self.Positions[1]=self.root # 1 is always root
if self.n>1:
for i in range(2,n+1):
self.Positions[i]=self.Node(None,[])
def joinNode(self,parent,node): # number of the two nodes , like 1-3 etc..
self.Positions[parent].children.append(self.Positions[node])
self.Positions[node].Parent=self.Positions[parent]
def joinAll(self):
for p,n in self.connections:
self.joinNode(p,n)
def Depth(self,p):
"""Return the number of levels separating Position p from the root."""
if p==self.root:
return 0
else:
return 1 + self.Depth(p.Parent)
def minDepth(self):
return min(self.Depth(self.Positions[p]) for p in self.leafnodes())
def leafnodes(self):
return list(set(x[0] for x in self.connections)^set(i for i in range(1,n+1)))
if __name__=='__main__':
n=int(input())
if n==1:
print(0)
else:
connections=[]
for i in range(1,n):
array=input()
edge=list(map(int,array.split()))
connections.append(edge)
mytree=tree(n,connections)
mytree.joinAll()
print(mytree.minDepth())
print(mytree.connections)
Sample input
5
1 4
4 2
2 3
3 5
sample output
4

Generating, traversing and printing binary tree

I generated perfectly balanced binary tree and I want to print it. In the output there are only 0s instead of the data I generated. I think it's because of the line in function printtree that says print(tree.elem), cause in the class self.elem = 0.
How can I connect these two functions generate and printtree?
class BinTree:
def __init__(self):
self.elem = 0
self.left = None
self.right = None
def generate(pbt, N):
if N == 0:
pbt = None
else:
pbt = BinTree()
x = input()
pbt.elem = int(x)
generate(pbt.left, N // 2)
generate(pbt.right, N - N // 2 - 1)
def printtree(tree, h):
if tree is not None:
tree = BinTree()
printtree(tree.right, h+1)
for i in range(1, h):
print(end = "......")
print(tree.elem)
printtree(tree.left, h+1)
Hope somebody can help me. I am a beginner in coding.
For example:
N=6, pbt=pbt, tree=pbt, h=0
input:
1
2
3
4
5
6
and the output:
......5
............6
1
............4
......2
............3
I'd suggest reading up on: https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/
Basically, there are three ways to traverse a binary tree; in-order, post-order and pre-order.
The issue with your print statement is that, you're reassigning the tree that is being passed in, to an empty tree.
if tree is not None:
tree = BinTree()
Right? If tree is not none and has something, lets reassign that to an empty tree.
Traversing a tree is actually a lot more simpler than you'd imagine. I think the complexity comes in just trying to imagine in your head how it all works out, but the truth is that traversing a tree can be done in 3 - 4 lines.

Categories

Resources