unable to implement recursion in binary tree - python-3.x

I just learned how to create a balanced binary tree and how to access them. For example, if this is my tree:
date = ((1,3,None),2,((None,3,4),5,(6,7,8)))
I can create it by given code:
class tree:
def __init__(self,key):
self.key = key
self.right = None
self.left = None
node_0 = tree(2)
node_1 = tree(3)
node_2 = tree(5)
node_3 = tree(1)
node_4 = tree(3)
node_5 = tree(7)
node_6 = tree(4)
node_7 = tree(6)
node_8 = tree(8)
#connecting
node_0.left = node_1
node_0.right = node_2
node_0.left.left = node_3
node_0.right.left = node_4
node_0.right.right = node_5
node_0.right.left.left = node_6
node_0.right.right.left = node_7
node_0.right.right.right = node_8
#accesing
node_0.right.right.right.key
But this method is very inefficient and lengthy so I tried recursion in place of that:
def parse_tuple(data):
if isinstance(data,tuple) and len(data)==3:
node = (data[1])
node.left = parse_tuple(data[0])
node.right = parse_tuple(data[2])
elif data is None:
node = None
else:
node=(data)
return node
so my idea is access data = ((1,3,None),2,((None,3,4),5,(6,7,8))) , by passing into a function but there's a problem
the function work well's till node.left = parse_tuple(data[0]). Basically here's what's going on
18:10:41.43 >>> Call to parse_tuple in File "C:\Users\muzza\AppData\Local\Temp\ipykernel_17864\3153403204.py", line 4
18:10:41.43 ...... data = ((1, 3, None), 2, ((None, 3, 4), 5, (6, 7, 8)))
18:10:41.43 ...... len(data) = 3
18:10:41.43 4 | def parse_tuple(data):
18:10:41.43 6 | if isinstance(data,tuple) and len(data)==3:
18:10:41.43 7 | node = (data[1])
18:10:41.43 .............. node = 2
18:10:41.43 8 | node.left = parse_tuple(data[0])
18:10:41.44 >>> Call to parse_tuple in File "C:\Users\muzza\AppData\Local\Temp\ipykernel_17864\3153403204.py", line 4
18:10:41.44 ...... data = (1, 3, None)
18:10:41.44 ...... len(data) = 3
18:10:41.44 4 | def parse_tuple(data):
18:10:41.44 6 | if isinstance(data,tuple) and len(data)==3:
18:10:41.44 7 | node = (data[1])
18:10:41.44 .............. node = 3
18:10:41.44 8 | node.left = parse_tuple(data[0])
18:10:41.44 >>> Call to parse_tuple in File "C:\Users\muzza\AppData\Local\Temp\ipykernel_17864\3153403204.py", line 4
18:10:41.44 ...... data = 1
18:10:41.44 4 | def parse_tuple(data):
18:10:41.44 6 | if isinstance(data,tuple) and len(data)==3:
18:10:41.44 10 | elif data is None:
18:10:41.45 13 | node=(data)
18:10:41.45 .............. node = 1
18:10:41.45 14 | return node
18:10:41.45 <<< Return value from parse_tuple: 1
18:10:41.45 8 | node.left = parse_tuple(data[0])
18:10:41.46 !!! AttributeError: 'int' object has no attribute 'left'
18:10:41.46 !!! When getting attribute: node.left
18:10:41.46 !!! Call ended by exception
18:10:41.46 8 | node.left = parse_tuple(data[0])
18:10:41.47 !!! AttributeError: 'int' object has no attribute 'left'
18:10:41.47 !!! When calling: parse_tuple(data[0])
18:10:41.47 !!! Call ended by exception
I tried to fix the issue but I can't think of any option available. I want to access the data that I've passed, just like the first method above mentioned. I've come across many recursion techniques to implement binary trees but I want correction in this code so that I can know my mistake.

The problem is that your code does not call the tree constructor. Your node should be an instance of that class, yet you merely assign the key to node.
So change node = (data[1]) to node = tree(data[1]) and node=(data) to node=tree(data)

Fixing your parse_tuple recursive function
Your function parse_tuple is almost correct, but you need to call tree() at each recursive call to create all the nodes.
def parse_tuple(data, node=None):
if isinstance(data, tuple) and len(data) == 3:
l, k, r = data
node = tree(k)
node.left = parse_tuple(l)
node.right = parse_tuple(r)
elif data is None:
node = None
else:
node = tree(data)
return node
parse_tuple( ((1, 3, None), 2, ((None, 3, 4), 5, (6, 7, 8))) )
Tree creation using Tree.__init__
There is a much simpler option.
Modify your method __init__ so that it accepts right and left arguments.
Instead of:
class tree:
def __init__(self,key):
self.key = key
self.right = None
self.left = None
I suggest to use instead:
class Tree:
def __init__(self, key, left=None, right=None):
self.key = key
self.left = left
self.right = right
This way, you can create the full tree directly:
node0 = Tree(2, Tree(3, Tree(1)), Tree(5, Tree(3, Tree(4)), Tree(7, Tree(6), Tree(8))))
Or perhaps more lisibly with some indentation:
node0 = Tree(2,
Tree(3,
Tree(1)),
Tree(5,
Tree(3, Tree(4)),
Tree(7, Tree(6), Tree(8))))

Related

I am getting runtime error in this code: https://leetcode.com/problems/range-sum-query-2d-immutable/submissions/

class NumMatrix:
def __init__(self, matrix: List[List[int]]):
m = len(matrix)
n = len(matrix[0])
PSUM = []
for i in range(m):
for j in range(1,n):
matrix[i][j] += matrix[i][j-1]
for i in range(n):
for j in range(1,m):
matrix[i][j]+= matrix[i-1][j]
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
ans = 0
ans += self.matrix[row2][col2]
if col1>=1:
ans+= self.matrix[row2][col1-1]
if row1>=1:
ans+= self.matrix[row1][col2]
if row1>=1 and col1>=1:
ans+= self.matrix[row1-1][col1-1]
return ans
# Your NumMatrix object will be instantiated and called as such:
# obj = NumMatrix(matrix)
# param_1 = obj.sumRegion(row1,col1,row2,col2)
#Added in the title I am getting runtime error in this code: https://leetcode.com/problems/range-sum-query-2d-immutable/submissions/
https://leetcode.com/problems/range-sum-query-2d-immutable/submissions/
Error: AttributeError: 'NumMatrix' object has no attribute 'matrix'
ans += self.matrix[row2][col2] Line 19 in sumRegion (Solution.py)
result = obj.sumRegion( Line 42 in helper_select_method (Solution.py)
ret.append(DriverSolution().helper_select_method(method, params[index], obj)) Line 83 in _driver (Solution.py)
_driver() Line 92 in (Solution.py)
In __init__, you are getting the matrix variable as an argument. That variable is in the function scope so it is limited to the function. In sumRegion, I assume you are trying to get that matrix variable from self, but you never set the variable.
This can be easily solved by inserting self.matrix = matrix in the first line of __init__

Nodes at given distance in binary tree (Amazon SDE-2)

Given a binary tree, a target node in the binary tree, and an integer value k, find all the nodes that are at distance k from the given target node. No parent pointers are available.
link to the problem on GFG: LINK
Example 1:
Input :
20
/ \
8 22
/ \
4 12
/ \
10 14
Target Node = 8
K = 2
Output: 10 14 22
Explanation: The three nodes at distance 2
from node 8 are 10, 14, 22.
My code
from collections import defaultdict
class solver:
def __init__(self):
self.vertList = defaultdict(list)
def addEdge(self,u,v):
self.vertList[u].append(v)
def makeGraph(self,root):
visited = set()
queue = []
queue.append(root)
while len(queue) > 0:
curr = queue.pop(0)
visited.add(curr)
if curr.left is not None and curr.left not in visited:
self.vertList[curr.data].append(curr.left.data)
self.vertList[curr.left.data].append(curr.data)
queue.append(curr.left)
if curr.right is not None and curr.right not in visited:
self.vertList[curr.data].append(curr.right.data)
self.vertList[curr.right.data].append(curr.data)
queue.append(curr.right)
def KDistanceNodes(self,root,target,k):
self.makeGraph(root)
dist = {}
for v in self.vertList:
dist[v] = 0
visited2 = set()
queue2 = []
queue2.append(target)
while len(queue2) > 0:
curr = queue2.pop(0)
visited2.add(curr)
for nbr in self.vertList[curr]:
if nbr not in visited2:
visited2.add(nbr)
queue2.append(nbr)
dist[nbr] = dist[curr] + 1
ans = []
for v in dist:
if dist[v] == k:
ans.append(str(v))
return ans
#{
# Driver Code Starts
#Initial Template for Python 3
from collections import deque
# Tree Node
class Node:
def __init__(self, val):
self.right = None
self.data = val
self.left = None
# Function to Build Tree
def buildTree(s):
# Corner Case
if (len(s) == 0 or s[0] == "N"):
return None
# Creating list of strings from input
# string after spliting by space
ip = list(map(str, s.split()))
# Create the root of the tree
root = Node(int(ip[0]))
size = 0
q = deque()
# Push the root to the queue
q.append(root)
size = size + 1
# Starting from the second element
i = 1
while (size > 0 and i < len(ip)):
# Get and remove the front of the queue
currNode = q[0]
q.popleft()
size = size - 1
# Get the current node's value from the string
currVal = ip[i]
# If the left child is not null
if (currVal != "N"):
# Create the left child for the current node
currNode.left = Node(int(currVal))
# Push it to the queue
q.append(currNode.left)
size = size + 1
# For the right child
i = i + 1
if (i >= len(ip)):
break
currVal = ip[i]
# If the right child is not null
if (currVal != "N"):
# Create the right child for the current node
currNode.right = Node(int(currVal))
# Push it to the queue
q.append(currNode.right)
size = size + 1
i = i + 1
return root
if __name__ == "__main__":
x = solver()
t = int(input())
for _ in range(t):
line = input()
target=int(input())
k=int(input())
root = buildTree(line)
res = x.KDistanceNodes(root,target,k)
for i in res:
print(i, end=' ')
print()
Input:
1 N 2 N 3 N 4 5
target = 5
k = 4
Its Correct output is:
1
And Your Code's output is:
[]
My logic:
-> First convert the tree into a undirected graph using BFS / level order traversal
-> Traverse the graph using BFS and calculate the distance
-> Return the nodes at k distance from the target
What I think:
First of all in the given test case the tree representation seems to be in level order however, in the failing test case it doesn't look like level order or maybe my logic is wrong?
Input Format:
Custom input should have 3 lines. First line contains a string representing the tree as described below. Second line contains the data value of the target node. Third line contains the value of K.
The values in the string are in the order of level order traversal of the tree where, numbers denote node values, and a character ā€œNā€ denotes NULL child.
For the above tree, the string will be: 1 2 3 N N 4 6 N 5 N N 7 N
The mistake is that the logic that is done in "init" should be done for every use-case (reinitializing self.vertList), not just once at the beginning.
Change:
def __init__(self):
self.vertList = defaultdict(list)
to:
def __init__(self):
pass
and:
def KDistanceNodes(self,root,target,k):
self.makeGraph(root)
dist = {}
...
to:
def KDistanceNodes(self, root, target, k):
self.vertList = defaultdict(list) # <-- move it here
self.makeGraph(root)
dist = {}
...
The original code kept accumulating the nodes and "remembered" the previous use-cases.
Also pay attention that you should return ans sorted, meaning that you should not append the numbers as strings so that you'll be able to sort them, change: ans.append(str(v)) to: ans.append(v) and return sorted(ans).

I want to trace the recursive code that I have written

I am novice to recursion, I have written code to find the path of a given node, when I dry run my code(trace the stack) it is giving the right answer but when I am running the same on machine it is not showing the expected output can someone please help me in tracing out the code(e.g. using the call stack)?
class NewNode:
def __init__(self, data):
self.data = data
self.left = self.right = None
arr = [1, 2, 3, 4, 5, 6, 7]
q = []
def create_level_order_binary_tree(i):
root = None
if i < len(arr):
root = NewNode(arr[i])
root.left = create_level_order_binary_tree(2 * i + 1)
root.right = create_level_order_binary_tree(2 * i + 2)
return root
def dfs(root, p, temp_path, path):
print(temp_path)
if root is None:
return path
if root.data == p:
if len(temp_path) == 0:
path.append(root.data)
return path
else:
temp_path.append(root.data)
path.append(temp_path)
return path
temp_path.append(root.data)
path = dfs(root.left, 6, temp_path, path)
if len(path) == 0:
path = dfs(root.right, 6, temp_path, path)
return path
root_node = create_level_order_binary_tree(0)
path_to_node = dfs(root_node, 6, [], [])
print(path_to_node`enter code here`)
The following are two approaches to solving your problem. While I haven't timed the routines, I suspect the non-recursive approach will be faster, since it doesn't utilize the stack as much.
First using a non-recursive approach employing a simple stack (Last In First Out) data structure.
from copy import deepcopy
def nrc_dfs(nde, p):
stck = [(nde, [])] #LIFO stack implementation
while stck:
nd, pth = stck.pop(len(stck)-1) #pop lastv entry
if nd:
pth.append(nd.data)
if nd.data == p:
return pth
stck.append((nd.right, deepcopy(pth)))
stck.append((nd.left, deepcopy(pth)))
return []
The second approach, using a recursive technique.
def rc_dfs(nde, p):
def dfs_sch(nde, p, path):
if nde:
path.append(nde.data)
if nde.data == p:
return path
pl = dfs_sch(nde.left, p, [])
if pl:
path.extend(pl)
return path
pr = dfs_sch(nde.right, p, [])
if pr:
path.extend(pr)
return path
return []
return dfs_sch(nde, p, [])

Error in regards to positional arguments in python while performing function overloading

I have been implementing function overloading using function to find the area of various figures(that includes square,circle and rectangle) but when I try to implement the concept here is the error that I get.
I made an instance of the class and then tried to call all the function with difference in the number of parameters. But still I am getting a lot of error.
from abc import abstractmethod
class Poly:
def __init__(self,n=0):
self._n = n
def area(self,a):
if isinstance(r,int):
return a ** 2
def area(self,b,pi):
if isinstance(a,float):
ar = pi * b * b
return ar
def area(self,l,r,ar):
if isinstance(l,int) and isinstance(r,int):
ar = l * r
return l*r
if __name__ == '__main__':
p = Poly()
pi = 3.14
ar = 0
l = 3
r = 4
b = 10
a = 2
peroi1 = p.area(a)
print(peroi1)
peroi2 = p.area(b,pi)
print(peroi2)
peroi3 = p.area(l,r,ar)
print(peroi3)
The expected output should give the area of square,circle and rectangle respectively but I get the following error.
Traceback (most recent call last):
File "overloadpractice.py", line 28, in <module>
peroi1 = p.area(a)
TypeError: area() missing 2 required positional arguments: 'r' and 'ar'
You can do something like this, but there are better ways. I'll come up with a better example in a little while:
class Poly:
def __init__(self, n=0):
self._n = n
def area(self, *args):
if len(args) == 1:
if isinstance(a, int):
return r ** 2
elif len(args) == 2:
if isinstance(a, float):
return pi * b * b
elif len(args) == 3:
if isinstance(l, int) and isinstance(r, int):
ar = l * r
return l*r
else:
return 'invalid number of arguments'
if __name__ == '__main__':
pi = 3.14
ar = 0
l = 3
r = 4
b = 10
a = 2
p = Poly()
peroi1 = p.area(a)
print(peroi1)
peroi2 = p.area(b, pi)
print(peroi2)
peroi3 = p.area(l, r, ar)
print(peroi3)
A better(easier to test and maintain) example:
from math import pi
SQUARE = 1
CIRCLE = 2
RECTANGLE = 3
get_area = {
SQUARE: lambda side : side * side,
CIRCLE: lambda radius, _ : pi * radius * radius,
RECTANGLE: lambda length, width, _ : length * width
}
class Poly:
def __init__(self, n=0):
self._n = n
def area(self, *args):
return get_area.get(len(args), lambda: 'invalid number of arguments')(*args)
if __name__ == '__main__':
ar = 0
l = 3
r = 4
b = 10
a = 2
# moved pi to an import
square = Poly()
circle = Poly()
rectangle = Poly()
print(square.area(a)) # 4
print(circle.area(b, pi)) # 14.1592653589793
print(rectangle.area(l, r, ar)) # 12
print(square.area()) # invalid number of variables

Using side_effect for class property doesn't work

I'm having trouble figuring out how to use the side_effect attribute of MagicMock but it only seems to work if I am patching functions. However, I would like to patch properties. Here's a minimal code example:
# in calendar.py
class Weeks():
a = property(lambda self: 0)
b = property(lambda self: 0)
c = property(lambda self: 0)
#patch('calendar.Weeks')
def test_something_else(weeks_mock):
weeks_mock.return_value.a.side_effect = [0, 10, 20, 30]
weeks_mock.return_value.b = 40
weeks_mock.return_value.c = 50
# some more code...
I've also unsuccessfully tried using PropertyMock like this:
#patch('calendar.Weeks')
def test_something_else(weeks_mock):
type(weeks_mock).a = PropertyMock(side_effect=[0, 10, 20, 30])
weeks_mock.return_value.b = 40
weeks_mock.return_value.c = 50
# some more code...
I would highly appreciate any input!
Use __get__ to mock and test the class attribute
get
Description
Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access).
with mock.patch.object(Weeks, 'a') as mock_a:
mock_a.__get__ = mock.Mock(return_value='mocked_cls_attr')
self.assertEqual(Weeks.a, 'mocked_cls_attr')
Not sure why you got the error, Here is a working test to help you figure out the eror.
import unittest
import mock
class Weeks():
a = property(lambda self: 0)
b = property(lambda self: 0)
c = property(lambda self: 0)
class TestWeeks(unittest.TestCase):
#mock.patch.object(Weeks, 'a')
#mock.patch.object(Weeks, 'b')
#mock.patch.object(Weeks, 'c')
def test_something_else(self, mockc, mockb, mocka):
# the attr a now mocka is becoming a function using side_effect
mocka.side_effect = [0, 10, 20, 30]
mockb.__get__ = mock.Mock(return_value=40)
mockc.__get__ = mock.Mock(return_value=50)
# mocka() not mocka as it is a function now
self.assertEqual(mocka(), 0)
self.assertEqual(mocka(), 10)
week = Weeks()
self.assertEqual(week.b, 40)
self.assertEqual(week.c, 50)
# week.a with using function will trigger a failure
# AssertionError: <MagicMock name='a' id='139943677051600'> != 20
self.assertEqual(week.a(), 20)
def test_property(self):
# no need to mock a property object, just replace it
week = Weeks()
week.a = property(lambda self: 1)
self.assertTrue(isinstance(week.a, property))
self.assertEqual(week.a.fget(0), 1)
Run test:
nosetests -v python2_unittests/tests/test_cls_attr.py
Results:
test_something_else (python2_unittests.tests.test_cls_attr.TestWeeks) ... ok
PropertyMock will work if you pass it as new_callable to a patch for each property:
class Weeks():
a = property(lambda self: 0)
b = property(lambda self: 0)
c = property(lambda self: 0)
#patch('__main__.Weeks.a', new_callable=PropertyMock)
#patch('__main__.Weeks.b', new_callable=PropertyMock)
#patch('__main__.Weeks.c', new_callable=PropertyMock)
def test_something_else(mock_c, mock_b, mock_a):
mock_a.side_effect = [0, 10, 20, 30]
mock_b.return_value = 40
mock_c.return_value = 50
# some more code...
weeks = Weeks()
assert weeks.a == 0
assert weeks.a == 10
assert weeks.b == 40
assert weeks.c == 50
In fact, your example with PropertyMock works for a. I just had to adapt b and c for it to completely work:
#patch('__main__.Weeks')
def test_something_else(weeks_mock):
type(weeks_mock).a = PropertyMock(side_effect=[0, 10, 20, 30])
type(weeks_mock).b = PropertyMock(return_value=40)
type(weeks_mock).c = PropertyMock(return_value=50)
# some more code...
assert weeks_mock.a == 0
assert weeks_mock.a == 10
assert weeks_mock.b == 40
assert weeks_mock.c == 50
And if you need more complicated logic, patch directly to a new property:
#patch('__main__.Weeks.a', property(lambda self: 40 if self.b else 50))
def test_something_complicated():
# some more code...
weeks = Weeks()
assert weeks.a == 50

Resources