Related
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))
Very new at Python and I'm trying to understand recursion over a binary tree. I've implemented a very simple tree, which funnily enough maps English characters to binary (1's and 0's). I've only used a very simple structure because I am struggling to get my head round a more complex question that I've been set. I figure if I can get my head round my example then I should be able to go away and look at the question I've been set myself.
The following creates the class BinaryTree and an instance of this
class BinaryTree:
"""A rooted binary tree"""
def __init__(self):
self.root = None
self.left = None
self.right = None
def is_empty(testtree: BinaryTree) -> bool:
"""Return True if tree is empty."""
return testtree.root == testtree.left == testtree.right == None
def join(item: object, left: BinaryTree, right: BinaryTree) -> BinaryTree:
"""Return a tree with the given root and subtrees."""
testtree = BinaryTree()
testtree.root = item
testtree.left = left
testtree.right = right
return testtree
EMPTY = BinaryTree()
C = join('C',EMPTY,EMPTY)
D = join('D',EMPTY,EMPTY)
E = join('E',EMPTY,EMPTY)
F = join('F',EMPTY,EMPTY)
A = join('A',C,D)
B = join('B',E,F)
BINARY = join('START',B,A)
I visualise it as follows
Visualisation of the Binary tree
Now I'm trying to create a function that will take two inputs, a BinaryTree and a single character and the output will be the binary code for the corresponding letter (as an example, D = " 10 "). I'm outputting as a string rather than an integer. My function and test case as follows
# global variable
result = ''
#Convert binary to letter
def convert_letter(testtree: BinaryTree, letter: str) -> str:
global result
if testtree == None:
return False
elif testtree.root == letter:
return True
else:
if convert_letter(testtree.left, letter) == True:
result += "1"
return result
elif convert_letter(testtree.right, letter) == True:
result += "0"
return result
#Test
test = 'D' #Return '10'
convert_letter(BINARY, test)
And unfortunately that's where I'm hitting a brick wall. I had tried initialising an empty string within the function, but everytime it iterates over the function it overwrites the string. Any help greatly appreciated.
The problem is that your function will sometimes return a boolean, sometimes a string, and sometimes None. So with this code:
if convert_letter(testtree.left, letter) == True:
result += "1"
return result
elif convert_letter(testtree.right, letter) == True:
result += "0"
return result
... you are not capturing all successful searches, as a successful search would return the actual string of "0" and "1" which obviously is not True. In that case the execution has no else to go to and returns None -- even when the letter was found in a deeper node.
Your function should not return a boolean -- that doesn't match the type hint either. It should be a string (the result). You could reserve None to indicate the letter was not found.
Some other problems:
result += "0" will append the digit, but since you already made the recursive call, you need to prepend the digit -- as you are higher up in the tree now.
The initialisation of your tree makes a different tree than you put in the image: A should be the left child, not the right child. So it should be join('START', A, B)
With those fixes, you'd have this code:
def convert_letter(testtree: BinaryTree, letter: str) -> str:
global result
if testtree is None:
result = None # Not found here
elif testtree.root == letter:
result = '' # Found! Start a path
elif convert_letter(testtree.left, letter) is not None:
result = "1" + result # Prepend
elif convert_letter(testtree.right, letter) is not None:
result = "0" + result # Prepend
else:
result = None # Not found here
return result
If you also correct to use join('START', A, B), then the output will be 10.
Better Practice
There are some things you can do better:
Don't use a global variable for storing the function result. As you return it, you can capture the result you get from a recursive call as a local variable, prepend to it, and return it again.
The definition of EMPTY makes your tree unnecessarily big. Just use None to denote an empty tree.
Don't call a node's value root. A rooted tree has only one root, and it is a node, not a value of a node. So call that attribute value or data, but not root.
The join function is nice, but why not use the constructor for that feature? The constructor can take those arguments as optional and immediately initialise the left and right attributes with those arguments.
The code-comment above the convert_letter function describes the opposite from what the function does.
Taking all that into account, your code could look like this:
class BinaryTree:
def __init__(self, value, left: 'BinaryTree'=None, right: 'BinaryTree'=None):
self.value = value
self.left = left
self.right = right
def convert_letter(tree: BinaryTree, letter: str) -> str:
if not tree:
return # Not found here, return None
if tree.value == letter:
return "" # Bingo: return an empty path
# No more global. path is a local variable
path = convert_letter(tree.left, letter)
if path is not None:
return "1" + path
path = convert_letter(tree.right, letter)
if path is not None:
return "0" + path
# Look how nice it is to create a tree using the constructor arguments
binary = BinaryTree("Start",
BinaryTree("A",
BinaryTree("C"), BinaryTree("D")
),
BinaryTree("B",
BinaryTree("E"), BinaryTree("F")
)
)
# Test
test = 'D'
print(convert_letter(binary, test)) # 10
I took the liberty of simplfying your code a bit let me know if you have any questions about how this works.
class node:
"""A rooted binary tree"""
def __init__(self, value = None, left = None, right = None):
self.value = value
self.left = left
self.right = right
C = node('C')
D = node('D')
E = node('E')
F = node('F')
A = node('A',C,D)
B = node('B',E,F)
BINARY = node('START',B,A)
def convert_letter(n,letter):
if n.value == letter:
return "1"+(convert_letter(n.left,letter) if not n.left is None else "")+(convert_letter(n.right,letter)if not n.right is None else "")
else:
return "0"+(convert_letter(n.left,letter) if not n.left is None else "")+(convert_letter(n.right,letter)if not n.right is None else "")
def walk(n):
return n.value+(walk(n.left) if not n.left is None else "")+(walk(n.right) if not n.right is None else "")
test = 'D'
print(convert_letter(BINARY, test))
print(walk(BINARY))
This is not how I would personally structure an answer, but I think it most closely follows what you are attempting. The shortcoming of your answer only being that you are only returning one value, but kind of tracking two values. Note, I have taken the liberty of correcting:
BINARY = join('START',A,B)
Let's modify your method to return both a Boolean indicating if the letter was found as well as the indicator of the path.
def convert_letter2(testtree: BinaryTree, letter: str):
if not testtree:
return (False, "")
if testtree.root == letter:
return (True, "")
test, val = convert_letter2(testtree.left, letter)
if test:
return (True, "1" + val)
test, val = convert_letter2(testtree.right, letter)
if test:
return (True, "0" + val)
return (False, "")
Then if we:
print(convert_letter2(BINARY, "D")[1])
We should get back "10"
I'm practicing creating a balanced binary search tree in python.
I already have these below, any idea on how to create a balance_bst funtion that passed a list of unique values that are
sorted in increasing order. It returns a reference to the root of a well-balanced binary search tree:
class LN:
def __init__(self,value,next=None):
self.value = value
self.next = next
def list_to_ll(l):
if l == []:
return None
front = rear = LN(l[0])
for v in l[1:]:
rear.next = LN(v)
rear = rear.next
return front
def str_ll(ll):
answer = ''
while ll != None:
answer += str(ll.value)+'->'
ll = ll.next
return answer + 'None'
# Tree Node class and helper functions (to set up problem)
class TN:
def __init__(self,value,left=None,right=None):
self.value = value
self.left = left
self.right = right
def height(atree):
if atree == None:
return -1
else:
return 1+ max(height(atree.left),height(atree.right))
def size(t):
if t == None:
return 0
else:
return 1 + size(t.left) + size(t.right)
def is_balanced(t):
if t == None:
return True
else:
return abs(size(t.left)-size(t.right)) <= 1 and is_balanced(t.left) and is_balanced(t.right)
def str_tree(atree,indent_char ='.',indent_delta=2):
def str_tree_1(indent,atree):
if atree == None:
return ''
else:
answer = ''
answer += str_tree_1(indent+indent_delta,atree.right)
answer += indent*indent_char+str(atree.value)+'\n'
answer += str_tree_1(indent+indent_delta,atree.left)
return answer
return str_tree_1(0,atree)
How do write the balance_bst?
def balance_bst(l):
Here is what I did:
def build_balanced_bst(l):
if l == None:
return None
else:
middle = len(l) // 2
return TN(l[middle],
build_balanced_bst(l[:middle]),
build_balanced_bst(l[middle + 1:]))
It gives me:
IndexError: list index out of range
How do I fix it?
I'm not going to write it for you since that's not what SO is about, but here's the general idea. Since the list is already sorted, the root should be the element in the middle of the list. Its left child will be the root of the balanced tree consisting of the elements to the left of the root in the list, and the right sub-tree will be the rest.
How would I be able to create common tree operations such as insert and search while passing in functions to reduce redundancy. For example, a recursive function calls itself on the left branch when the value passed in is greater than the current node. If I were able to pass in functions like insert and search, I'd be able to factor out a lot of the traversal. The main problem area I'm seeing is that both functions have different base cases. Example solutions in python would be appreciated.
def insert(n, node = root):
if node == None:
node.data = n
node.left, node.right, node.middle = None
elif node.data == n:
insert(node.middle)
elif node.data < n:
insert(right)
else:
insert(left)
def search(n, node = root):
if node == None:
return false
elif node.data == n:
return true
elif node.data < n:
search(right)
else:
search(left)
Your logic for inserting is not correct. You are trying to set attributes on None.
For reusing common code you can use function decorators. Implement common code for traversing tree in decorator function and action on found element in operation function. You can change your code as follow:
def tree_operation(f):
def recursive_wrapper(n, node):
if node == None or node.data == n:
# tree traversed to final point. do action for found element or
# None
return f(n, node)
# try getting closer to interesting element
elif node.data < n:
return recursive_wrapper(n, node.right)
else:
return recursive_wrapper(n, node.left)
return recursive_wrapper
#tree_operation
def search(n, node):
if node == None:
return False
elif node.data == n:
return True
#tree_operation
def insert(n, node):
if node == None:
# this obviously fail
node.data = n
node.left, node.right, node.middle = None
elif node.data == n:
insert(node.middle)
In fact it passes functions, as you noted in question plus renames resultant function to passed in function. Above decorator syntax for insert function does this:
insert = tree_operation(insert)
I think its better to not combine the iterative part of functions in a single one because it complicates the code. But if you think that the iterative part is complex and you need to write it once, you can re-factor it as follow:
def do_iterate(base_function, n, node=root):
if node == None or node.data == n:
base_function(n, node)
elif node.data < n:
do_iterate(base_function, n, node.right)
else:
do_iterate(base_function, n, node.left)
Then, you can write your own base_function that will be called when the base conditions met. For example you can use base_insert() and base_search() functions in place of insert() and search() functions.
def base_insert(n, node):
if node == None:
node.data = n
node.left, node.right, node.middle = None
else:
do_iterate(base_insert, n, node.middle)
def base_search(n, node):
if node == None:
return false
else:
return true
So you can use your algorithms like below:
do_iterate(base_insert, 7)
do_iterate(base_search, 4)
At the end, I'm not sure it's better than your simple code.
Me and my friend are doing some school work with programming in Python 3.1 and are VERY stuck. We're programming a binary tree and it's working fine except when we want to print all the nodes in inorder in a way that would create a sentence (all the words in inorder just after one another in a row). We have been looking all over the internet for clues as to how to procede and we've been working with this little thing for like two hours. Any advice/help would be awesome.
Our program/Binary tree:
class Treenode:
def __init__(self, it = None, le = None, ri = None):
self.item = it
self.left = le
self.right = ri
class Bintree:
def __init__(self):
self.item = None
self.left = None
self.right = None
def put(self, it = None):
key = Treenode(it)
if self.item == None:
self.item = key
return
p = self.item
while True:
if key.item < p.item:
if p.left == None:
p.left = key
return
else:
p = p.left
elif key.item > p.item:
if p.right == None:
p.right = key
return
else:
p = p.right
else:
return
def exists(self, it):
key = it
p = self.item
if p == key:
return True
while True:
if key < p.item:
if p.left == None:
return False
else:
p = p.left
elif key > p.item:
if p.right == None:
return False
else:
p = p.right
else:
return
def isEmpty(self):
if self.item == None:
return True
else:
return False
def printtree (Treenode):
if Treenode.left != None:
printtree (Treenode.left)
print (Treenode.item)
if Treenode.right != None:
printtree (Treenode.right)
We get a sort of print when we run the program which looks like this: "bintree.Treenode object at 0x02774CB0", which is not what we want.
We use the tree by running this:
import bintree
tree = bintree.Bintree()
print(tree.isEmpty()) # should give True
tree.put("solen")
print(tree.isEmpty()) # should give False
tree.put("gott")
tree.put("sin")
tree.put("hela")
tree.put("ban")
tree.put("upp")
tree.put("himlarunden")
tree.put("manen")
tree.put("seglar")
tree.put("som")
tree.put("en")
tree.put("svan")
tree.put("uti")
tree.put("midnattsstuden")
print(tree.exists("visa")) # should give False
print(tree.exists("ban")) # should give True
tree.printtree() # print sorted
Also, the second last row gives us "None" instead of "True", which is wierd.
To print a binary tree, if you are printing a leaf you just print the value; otherwise, you print the left child then the right child.
def print_tree(tree):
if tree:
print tree.value
print_tree(tree.left)
print_tree(tree.right)
print(tree.exists("visa")) returns None, because in the last line of exists() there's return statement without any value (which defaults to None).
Also you shouldn't name a printtree argument Treenode since it's a name of an existing class and that might lead to confusion. It should look more like:
def printtree(tree_node):
if tree_node.left is not None:
printtree(tree_node.left)
print(tree_node.item)
if tree_node.right is not None:
printtree(tree_node.right)
Another thing is calling printtree - it's a function, not Bintree method, so I suppose you should call it printtree(tree).
One way to make testing easier is to use -assert()- instead of printing things and then referring back to your code.
tree = Bintree()
assert(tree.isEmpty())
tree.put("solen")
assert(not tree.isEmpty())
tree.put("gott")
tree.put("sin")
tree.put("hela")
tree.put("ban")
http://docs.python.org/reference/simple_stmts.html#the-assert-statement
It raises an error if its condition is not true. I know that doesn't fix your bug but making things less ambiguous always helps debugging.
You are not specifying a starting case for printtree(). You're defining how to recurse through your tree correctly, but your call to printtree() has no node to start at. Try setting a default check to see if a parameter is passed in, and if one isn't start at the head node of the bintree.
The reason your second to last line is printing None is because, in your exists method, you just have a "return", rather than a "return True", for the case of finding a `p.item' that is equal to key.