Discrepancy of list append in python - python

I am getting a different result when I am using append(path) vs. append(list(path))
I have the following code to find all paths for a sum:
class TreeNode:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def find_paths(root, sum):
allPaths = []
dfs(root, sum, [], allPaths)
return allPaths
def dfs(root, sum, path, res):
if not root:
return
path.append(root.val)
if root.val == sum and root.left is None and root.left is None:
res.append(path)
dfs(root.left, sum - root.val, path, res)
dfs(root.right, sum - root.val, path, res)
del path[-1]
def main():
root = TreeNode(12)
root.left = TreeNode(7)
root.right = TreeNode(1)
root.left.left = TreeNode(4)
root.right.left = TreeNode(10)
root.right.right = TreeNode(5)
sum = 23
print("Tree paths with sum " + str(sum) +
": " + str(find_paths(root, sum)))
main()
This has the following output:
Tree paths with sum 23: [[], []]
But if I change the res.append(path) to res.append(list(path)) which will then return the correct answer Tree paths with sum 23: [[12, 7, 4], [12, 1, 10]]. I am confused on why using the list operation would change the answer.

res.append(path) appends the object path itself to the list res. After that line, when you modify the path object (like del path[-1]), the modification is also applied to the appended object in res, because, well, they are the same object.
list(path), on the other hand, "copies" the path. So this one is now a different object from path. When you modify path after that, the modification does not propagates to this different object.
You will have the same result if you do path[:] or path.copy() instead of list(path).

res.append(path) appends the actual path object, not a copy of it. So if path changes later on, the change will appear in res also.
res.append(list(path)) appends a copy.

Related

LeetCode expects null instead of None. How to do this in python?

I'm currently working on LeetCode problem 108. Convert Sorted Array to Binary Search Tree:
Given an integer array nums where the elements are sorted in ascending order, convert it to a height-balanced binary search tree.
A height-balanced binary tree is a binary tree in which the depth of the two subtrees of every node never differs by more than one.
My code seems to be working fine but I don't know how to display the value null instead of None in my list. I need to print the BST in level order traversal. I'm looking for advice, hints or suggestions.
Input:
[-10,-3,0,5,9]
My current output:
[0, -3, 9, -10, None, 5, None]
Accepted output:
[0,-3,9,-10,null,5]
Here is my code:
from queue import Queue
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def sortedArrayToBST(nums: [int]) -> Optional[TreeNode]:
nbNodes = len(nums)
if nbNodes == 1:
root = TreeNode()
root.val = nums[0]
return root
elif nbNodes == 0:
root = TreeNode()
root.val = None
return root
middle = int(nbNodes / 2)
root = TreeNode()
root.val = nums[middle]
leftList = []
rightList = []
j = middle + 1
for i in range(middle):
leftList.append(nums[i])
if j != nbNodes:
rightList.append(nums[j])
j += 1
root.left = sortedArrayToBST(leftList)
root.right = sortedArrayToBST(rightList)
return root
def levelorder(root):
if root==None:
return
Q=Queue()
Q.put(root)
level_order_list = []
while(not Q.empty()):
node=Q.get()
if node==None:
continue
level_order_list.append(node.val)
Q.put(node.left)
Q.put(node.right)
print(level_order_list)
if __name__ == "__main__":
container = [-10,-3,0,5,9]
levelorder(sortedArrayToBST(container))
This is kind of a weird requirement that has nothing to do with the apparent main point of the problem and I suspect it's a result of the description being copied over from one that's aimed at another language (like Javascript, which uses null instead of None).
You can, however, format your list however you like when you print it; here's an example where we print a list by joining each item with "," (instead of the default ", ") and replace None with "null":
>>> my_list = [0, -3, 9, -10, None, 5, None]
>>> print("[" + ",".join("null" if i is None else str(i) for i in my_list) + "]")
[0,-3,9,-10,null,5,null]
Since JSON renders None as null, another option would be to dump the list to JSON and remove the " " characters:
>>> import json
>>> print(json.dumps(my_list).replace(' ', ''))
[0,-3,9,-10,null,5,null]
The problem is not related to null or None. LeetCode is a platform for taking code challenges in many different languages and they use JSON style to describe input/output, and in JSON notation None translates to null.
Not to worry about that. However, when you look at your output, there is a trailing None that should not be there. That means that you returned a BST that has a node with a None value. This should not happen.
The code that creates this None valued node is easy to identify... it is here:
elif nbNodes == 0:
root = TreeNode()
root.val = None
return root
return
When you think of it, this cannot be right: the number of nodes to generate (nbNodes) is 0, yet your code creates 1 node -- and that is one too many! In this case you should just return None to indicate that the parent node has no child here.
So replace with:
elif nbNodes == 0:
return
This fixes the issue you mentioned, and your code will now pass all tests on LeetCode.
Here is your code with the above corrections and with the self parameter restored (which you had removed to run it without the need to create a class instance):
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
nbNodes = len(nums)
if nbNodes == 1:
root = TreeNode()
root.val = nums[0]
return root
elif nbNodes == 0:
return
middle = int(nbNodes / 2)
root = TreeNode()
root.val = nums[middle]
leftList = []
rightList = []
j = middle + 1
for i in range(middle):
leftList.append(nums[i])
if j != nbNodes:
rightList.append(nums[j])
j += 1
root.left = self.sortedArrayToBST(leftList)
root.right = self.sortedArrayToBST(rightList)
return root
Other improvements
Unrelated to your question, but there are several things you can optimise:
Try to avoid creating new lists (leftList, rightList): copying values into them takes time and space. Instead use start/end indices in the original list to denote which sublist is currently processed.
Make use of the additional arguments you can pass to the TreeNode constructor. That way you don't have to assign to attributes after the constructor has run.
Use the integer division operator instead of the floating point division operator.
One of the two base cases is not needed as it would be dealt with correctly by the next recursive calls.
Here is a spoiler solution that applies those remarks:
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
def recur(start: int, end: int):
if start < end:
middle = (start + end) // 2
return TreeNode(nums[middle],
recur(start, middle),
recur(middle + 1, end))
return recur(0, len(nums))

Recursive function does not return array in python

I'm writing a simple code that returns the path to the destination node in BST.
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
root = TreeNode(6)
root.left = TreeNode(2)
root.right = TreeNode(8)
root.left.left = TreeNode(0)
root.left.right = TreeNode(4)
root.left.right.left = TreeNode(3)
root.left.right.right = TreeNode(5)
root.right.left = TreeNode(7)
root.right.right = TreeNode(9)
After defining the tree;
p = 2
q = 8
def pathFind(path, cur, node): # path : path the function walked throug so far
# cur : current node
# node : destination node's value
#print(f'current node value is {cur.val}')
#print(path)
## ending condition ##
if cur.val == node: # If we reach the destination node
return path
elif cur.val < node and cur.right != None :
# 'if cur.right != None:' line is useless since the problem guarantees the existence of destination value in BST
path.append(cur)
return pathFind(path, cur.right, node)
elif cur.val > node and cur.left != None: # cur.val > node:
path.append(cur)
return pathFind(path, cur.left, node)
else:
return None
path_p = pathFind([root], root, p)
I checked that my function reaches the destination and record the path toward it without any problem, but the last line - path_p = pathFind([root], root, p) doesn't work.
Anyone could help?
In function pathFind(), the path is returned only by the execution where the tested node contains the target value. The (all) previous executions discard that return value. Fix it by putting return before the recursive calls.
Try below code to find the path for a target node
def pathFind(path, node, target):
if node == None:
return
else:
path.append(node.val)
if node.val == target:
return path
elif node.val < target and node.right != None:
return pathFind(path, node.right, target)
elif node.val > target and node.left != None:
return pathFind(path, node.left, target)
else:
return None
print(pathFind([], root, 9))
output
[6, 8, 9]
A few remaining issues (after the edits to your question):
The root node is in most cases added twice to the path: once in the initial call, and again when moving to the left or right subtree of the root node: cur is the root node and path.append(cur) is executed.
So, pass an empty list in the initial call instead of [root]
When the target node is found, that node is not appended to the path, yet I suppose it should be the final node in the path. So path.append(cur) should also happen in the first if block.
Those changes will fix your code.

Finding the closest number in a Binary Tree to a given target

I have a very simple Binary Tree
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
root = TreeNode(8)
root.left = TreeNode(5)
root.right = TreeNode(14)
root.left.left = TreeNode(4)
root.left.right = TreeNode(6)
root.left.right.left = TreeNode(8)
root.left.right.right = TreeNode(7)
root.right.right = TreeNode(24)
root.right.right.left = TreeNode(22)
and I implemented a function to find the closest number in the tree to the target (19):
def closest_value(root, target, closest=0):
if abs(root.val - target) < abs(closest - target):
closest = root.val
print(closest)
if root.left is not None:
closest_value(root.left, target, closest)
if root.right is not None:
closest_value(root.right, target, closest)
return closest
The result should be obviously 22, but instead i get 8. Surprisungly, when I print all the following 'closest' numbers, the function seems to be working fine: It prints: 8, 14, 22. But why doesn't it return the latest clostest number: 22?
result = closest_value(root, 19)
print('result:', result)
The value of closest in the first call to closest_value is not updated in the if-statements. Simply assign the value to closest:
def closest_value(root, target, closest=0):
if abs(root.val - target) < abs(closest - target):
closest = root.val
if root.left is not None:
#assign value
closest = closest_value(root.left, target, closest)
if root.right is not None:
#assign value
closest = closest_value(root.right, target, closest)
return closest
result = closest_value(root, 19)
print(result)
# 22
You are not using the result of your recursive calls to determine the final returned value.
Perhaps a simpler approach, without pushing down a defaulted parameter would be easier:
def closest(node,value):
if not node: return float('inf')
vals = [node.val, closest(node.left,value), closest(node.right,value)]
return min(vals,key=lambda v:abs(v-value))
closest(root,19) # 22
One issue, is that this is an O(n) approach that will go through the whole binary tree without leveraging the hierarchy. For a sorted binary tree, you can get a O(logN) solution, by implementing a pair of binary search functions to find the closest node with a value that is <= and the closest node with a value that is >=. Then only apply the absolute value comparison between these two nodes that will have been found in O(logN) time.
def findLE(node,value):
if not node: return None
if node.val == value: return node
if node.val<value: return findLE(node.right,value) or node
return findLE(node.right,value)
def findGE(node,value):
if not node: return None
if node.val == value: return node
if node.val>value: return findGE(node.left,value) or node
return findGE(node.right,value)
def closestValue(node,value):
less = findLE(node,value)
more = findGE(node,value)
if more and less:
return min(more.val,less.val,key=lambda v:abs(v-value))
return (more or less).val
Note that your binary tree is not in sorted order because of the 8 that is left of node 6:
8
__/ \_
5 14
/ \ \
4 6 24
/ \ /
8 7 22
(you can find the binary tree printing function here)

How come these 2 implementations of dfs give different results?

This one gives the correct result:
def binaryTree(root):
paths = []
def dfs(root, path=""):
if root:
if path != "":
path += "->"
path += str(root.val)
if not root.left and not root.right:
paths.append(path)
dfs(root.left, path)
dfs(root.right, path)
dfs(root)
return paths # gives ['1->2->4', '1->2->5', '1->3']
And in this one the list of path keeps growing:
def binaryTree2(root):
paths = []
def dfs(root, path=[]):
if root:
path.append(root.val)
if not root.left and not root.right:
paths.append("->".join(map(str, path)))
dfs(root.left, path)
dfs(root.right, path)
dfs(root)
return paths # gives ['1->2->4', '1->2->4->5', '1->2->4->5->3']
The tree is like this: <1, <2, <4, None, None>, <5, None, None>>, <3, None, None>>
The only difference is that in one I concatenate strings and in the other I append to list.
So in the first implementation: All path += ... statements essentially create a new string and have path point to it.
As for the second implementation you have a single list that is passed around all the time. You should pop back the node right before dfs returns.
def binaryTree2(root):
paths = []
def dfs(root, path=[]):
if root:
path.append(root.val)
if not root.left and not root.right:
paths.append("->".join(map(str, path)))
dfs(root.left, path)
dfs(root.right, path)
path.pop() # this clears your stack as your functions return
dfs(root)
return paths
Edit: Python strings are immutable - i.e. once created, they can't be modified.
# below line essentially creates a pointer,
# and a string object that `path` points to.
path = "huhu"
# this creates another string object `huhu123`.
# So at this point we have 3 strings objects,
# "123", "huhu" and "huhu123". And a pointer `path` if you will.
# `path` points to "huhu123"
path += "123"
If we had more innocent objects instead of strings, once they are left with no references, they'd be garbage collected. Strings get special treatment, in our case all 3 of them are interned.

Binary Tree Level Order Traversal in Python

Given the following tree:
I should return the level order traversal of the tree from left to right:
so the above example would output a list of lists :
[ [3], [9,20], [15,7] ]
I wrote the following code, idea is storing the node value and its depth recursively in a Queue then iterate the Queue tuples and put the in intermediate list O if no more node of same depth append O to output and empty O and so on. However my code Timeout any help?
import queue
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
def helper(root,res,level):
if not root:
return res
l=level+1
res.put((root.val,level))
helper(root.left,res,l)
helper(root.right,res,l)
res=queue.Queue()
helper(root,res,0)
d=1
output=[]
node,depth=res.get()
output.append([node])
while res:
o=[]
node,depth=res.get()
while d ==depth:
o.append(node)
node,depth=res.get()
else:
d+=1
output.append(o)
return output
Here is my code for the Breadth First Search (BFS) Iterative Implementation with nodes in each level in the final output is under a single list:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def BFS(self, root) -> int:
level=1
current=(root, level)
s=set()
result=[]
Q = [current]
while Q:
current=Q.pop()
level=current[1]
if current[0] not in s:
result.append([current[0].val, level])
s.add(current[0])
if current[0].left:
Q.insert(0,(current[0].left, level+1))
if current[0].right:
Q.insert(0,(current[0].right, level+1))
output=[]
temp=[]
level=1
for val in result:
if val[1]==level:
temp.append(val[0])
elif val[1] > level:
output.append(temp)
temp=[val[0]]
level+=1
output.append(temp)
return output
Testing:
n1=TreeNode(3)
n2=TreeNode(9)
n3=TreeNode(20)
n4=TreeNode(6)
n5=TreeNode(15)
n6=TreeNode(7)
n1.left=n2
n1.right=n3
n2.left=n4
n3.left=n5
n3.right=n6
sol1=Solution()
print(sol1.BFS(n1))
[[3], [9, 20], [6, 15, 7]]
Thanks for the answer from san. The solution helps me solve problem 107 of leetcode. I revised a little bit based on my understanding. There are two ways to solve this question but I prefer to use BFS fashion. This revised version compresses both value and level in the queue. It provides more flexibility comparing with only print the value of each node in the tree, as other tutorials provided.
# 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 levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
if root is None:
return None
level = 1
q = []
q.append((root,level)) # push both node and level.
save = []
while q:
cur = q.pop(0)
cur_node = cur[0]
cur_level = cur[1]
# print(cur_node.val)
# print(cur_level)
save.append([cur_node.val, cur_level])
if cur_node.left:
q.append((cur_node.left, cur_level+1))
if cur_node.right:
q.append((cur_node.right, cur_level+1))
print(save) # once print, you will have the idea about how to reorgnized the required output.
level = 1
output = []
temp = []
for i in range(len(save)):
cur = save[i]
#print(cur)
if cur[1] == level:
temp.append(cur[0])
if cur[1] != level:
output.insert(0, temp)
temp = []
temp.append(cur[0])
level = level + 1
if i == len(save)-1:
output.insert(0, temp)
return output
You can use the below logic , to print out nodes in BFS .
If you also need , you modify the method to return a list as well .
def levelOrder(root):
qroot = []
print(root.info,end=' ')
if root.left:
qroot.append(root.left)
if root.right:
qroot.append(root.right)
while(qroot):
tmp = qroot[0]
if tmp.left:
qroot.append(tmp.left)
if tmp.right:
qroot.append(tmp.right)
print (tmp.info,end=' ')
qroot.pop(0)
return
Instead of printing the values , you can directly keep appending the value to a new list and return the same .

Categories