Why does my binary tree behave differently given different byte strings? - python

I have been practicing recursion with python and currently am attempting to stop recursing all the way down to single bytes and instead stop at a certain byte size. In this example I choose 2, so in my code if a either of the potential children to be spawned is less than 2, it won't recurse and will just return the current node. It works fine with the first byte string, but fails with the next two. Why is this happening and how can I fix it?
Correct output for 1st b: stops recursing/creating children at size 3, because next generation of children have at least 1 child smaller than
size 2
b'\x00\x01\x00\x02\x00\x03'
b'\x00\x01\x00'
b'\x02\x00\x03'
Incorrect output for 2nd b: Appears to be recursing until single bytes
b'L_]ju\x87\xd4\x14j\x1b> \xc52'
b'L_]ju\x87\xd4'
b'L_]'
b'ju\x87\xd4'
b'ju'
b'\x87\xd4'
b'\x14j\x1b> \xc52'
b'\x14j\x1b'
b'> \xc52'
b'> '
b'\xc52'
from random import randbytes
class Node:
def __init__(self, value):
self.value = value
self.children = []
self.parent = None
self.bytesize = len(value)
def make_children(self, child):
child.parent = self
self.children.append(child)
def print_tree(self):
print(self.value)
if len(self.children) > 0: # leaf node case
for child in self.children:
child.print_tree()
def build_tree(value_list):
root = Node(value_list)
#if len(value_list) == 1:
if len(value_list) / 2 < 2: # MODIFY TO STOP RECURSING IF SIZE OF CHILDREN WILL BE BELOW 2
return root
mid_point = len(value_list) // 2
left_half = value_list[:mid_point]
right_half = value_list[mid_point:]
child1 = build_tree(left_half)
root.make_children(child1)
child2 = build_tree(right_half)
root.make_children(child2)
return root
if __name__ == '__main__':
#list1 = [12, 7, 8, 15, 9]
b = b'\x00\x01\x00\x02\x00\x03'
#b = b'\x4c\x5f\x5d\x6a\x75\x87\xd4\x14\x6a\x1b\x3e\x20\xc5\x32'
#b = randbytes(6)
file = build_tree(b)
file.print_tree()
print(len(b))

Your code is actually working as intended. The two byte strings you mention both have 2 bytes, not 1.
Here is one way to display a bytestring that might make it more clear:
def print_string(s):
print(' '.join(map('{:#2x}'.format, s)))
print_string(b'> ')
# 0x3e 0x20
print_string(b'\xc52')
# 0xc5 0x32

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))

Need help finishing binary tree python code

You will be adding to the classes Node and Tree that we developed in our lectures. There are several short methods that you will have to write.
Write a method is_similar() that takes as input two binary trees and returns true if the nodes have the same key values and are arranged in the same order and false otherwise.
def is_similar (self, pNode):
Write a method print_level() that takes as input the level and prints out all the nodes at that level. If that level does not exist for that binary search tree it prints nothing. Use the convention that the root is at level 1.
def print_level (self, level):
Write a method get_height() that returns the height of a binary tree. Recall that the height of a tree is the longest path length from the root to a leaf.
def get_height (self):
Write a method num_nodes() that returns the number of nodes in the left subtree from the root and the number of nodes in the right subtree from the root and the root itself. This function will be useful to determine if the tree is balanced.
def num_nodes (self):
Input: The input will read from a file. The file will be formatted as follows:
Line 1: Several integers separated by spaces to be inserted into Tree 1
Line 2: Several integers separated by spaces to be inserted into Tree 2
You will read both lines of data. Create two Trees and insert the integers in the order given. Then you will use these two trees to test the methods that you have written.
Output: The output will be formatted as follows:
The Trees are similar: (True or False)
Levels of Tree 1:
print each level on its own line
Levels of Tree 2:
print each level on its own line
Height of Tree 1:
Nodes in Tree 1:
Height of Tree 2:
Nodes in Tree 2:
For example, given the following input file:
14 17 1
14 17 1
This would be the output:
The Trees are similare: True
Levels of Tree 1:
14
1 17
Levels of Tree 2:
14
1 17
Height of Tree 1: 1
Nodes in Tree 1: 3
Height of Tree 2: 1
Nodes in Tree 2: 3
You will be writing helper methods for the Tree class that we developed. The following is the outline of the code that you will be submitting. You may include other functions that we developed for completeness. You may add helper functions as needed.
Below is the code that I need help finishing. Not entirely sure how to start the helper functions or main so any help would be appreciated.
import os
class Node (object):
def __init__ (self, data):
self.data = data
self.lchild = None
self.rchild = None
class Tree (object):
def __init__ (self):
self.root = None
# insert data into the tree
def insert (self, data):
new_node = Node (data)
if (self.root == None):
self.root = new_node
return
else:
current = self.root
parent = self.root
while (current != None):
parent = current
if (data < current.data):
current = current.lchild
else:
current = current.rchild
# found location now insert node
if (data < parent.data):
parent.lchild = new_node
else:
parent.rchild = new_node
# Returns true if two binary trees are similar
def is_similar (self, pNode):
pass
# Prints out all nodes at the given level
def print_level (self, level):
pass
# Returns the height of the tree
def get_height (self):
pass
# Returns the number of nodes in tree which is
# equivalent to 1 + number of nodes in the left
# subtree + number of nodes in the right subtree
def num_nodes (self):
pass
def main():
# write code here
main()
As a hint, think of how you would need to traverse the binary tree in the implementation of each helper method.
For num_nodes, I am not sure what "and the number of nodes in the right subtree from the root and the root itself." means. Should we return the number of nodes in the right subtree + 1?
#classmethod
def count_below(node):
count=0
if (node == None):
return 0 # if one of the root's childs was None
if (node.lchild == None and node.rchild == None): # leaf
return 1 # base case
if (node.lchild != None):
count+=count_below(node.lchild)
if (node.rchild != None):
count+=count_below(node.rchild)
return count
def num_nodes(self):
if (self.root == None):
return 0
return count_below(self.root.lchild), count_below(self.root.rchild) + 1
#classmethod
def depth_below(node):
if node is None:
return 0 # base case
# Compute the depth of each subtree
ldepth = depth_below(node.lchild) # recurse left
rdepth = depth_below(node.rchild) # recurse right
# once all the recursive calls performed on this node's childs resolve
# return the depth of the subtree of this node with the greater depth
if (ldepth > rdepth):
return ldepth+1
else:
return rdepth+1
def get_height(self):
return depth_below(self.root) # depth from root
#classmethod
def explore_childs(node, current_level, target_level):
if (node.lchild==None and node.rchild==None):
return # base case
if (current_level == target_level):
if (node.lchild!=None):
print(node.lchild.data)
if (node.rchild!=None):
print(node.rchild.data)
return # base case
if (node.lchild!=None):
explore_childs(node.lchild, current_level+1, target_level) # recurse left
if (node.rchild!=None):
explore_childs(node.rchild, current_level+1, target_level) # recurse right
def print_level(self, level):
if (level > self.get_height()):
pass # throw error
explore_childs(root, 0, level)

Not to add duplicate values to Binary tree in python (not BST)

I am trying to create a binary tree from an array of values. I dont want to add a duplicate entry to the tree, if a duplicate value is found, I need to increment the counter of existing node.
class eNode:
def __init__(self, data):
self.data = data
self.left = self.right = None
self.Counter = 1
def __str__(self):
return str(self.data) + " Counter: " + str(self.Counter)
def insertLevelOrder(arr, root, i, n):
if i < n:
temp = eNode(arr[i])
root = temp
root.left = insertLevelOrder(arr, root.left,
2 * i + 1, n)
root.right = insertLevelOrder(arr, root.right,
2 * i + 2, n)
return root
def main():
empList = [1, 2, 2, 3, 5]
n = len(empList)
root = None
root = insertLevelOrder(empList, root, 0, n)
print (root);
print (root.left);
print (root.right);
print (root.left.left);
print (root.left.right);
#inOrder(root)
main()
Is there a way to achieve this ? all helps are appreciated.
Since your tree building algorithm assigns a value to a specific location in the tree, that only depends on the index in the input array, it becomes a problem when certain values would not become a node in a tree (but would increment a counter of some other node):
The would-be children of that value will be orphaned.
Take the example:
[1, 2, 2, 3, 5, 6]
Without any special treatment for duplicates, this produces the following tree:
1
/ \
2 2
/ \ /
3 5 6
The fact that 6 ends up on the third level, at the 3rd place, is completely determined by the fact that this value occurs at index 5 in the input array (zero-based indexing), and nothing else. If we would not create the node for the second 2, we would get an orphaned 6:
1
/
2
/ \
3 5 6
One way to solve this, is to agree that the index in the original input array is no longer defining the position of the node, and the tree would become:
1
/ \
2* 3
/ \
5 6
...where the asterisk represents a count of 2. Note that this is a completely different tree. For instance, now 3 is a direct child of 1, and only because there was a duplicate 2...
If this is however how you would want it to work, then make your algorithm iterative instead of recursive, and keep track of what the parent is of any newly created node:
class eNode:
def __init__(self, data):
self.data = data
self.left = self.right = None
self.counter = 1
def __str__(self):
return str(self.data) + " Counter: " + str(self.counter)
def insertLevelOrder(arr):
if arr == []:
return
root = eNode(arr[0])
nodes = [root]
d = {root.data: root}
right = False
parent = 0
for val in arr[1:]:
if val in d:
d[val].counter += 1
else:
node = eNode(val)
nodes.append(node)
d[val] = node
if right:
nodes[parent].right = node
parent += 1
else:
nodes[parent].left = node
right = not right
return root
def main():
empList = [1, 2, 2, 3, 5]
root = insertLevelOrder(empList)
print (root);
print (root.left);
print (root.right);
print (root.left.left);
print (root.left.right);
main()
You can maintain a dictionary of value to binary tree nodes. If the value being inserted already exists in the dictionary, you can get the reference to the node and update it.

Building a Binary Search Tree from a file

I have a text file of lines in the format
2 0 0
7 0 0
4 1 1
10 0 0
9 0 1
8 1 1
These lines represent the data in a binary search tree where the first element is the node data, the second is whether or not a left child exists ( 0 if no, 1 if yes) and the third is whether or not a right child exists (0 if no, 1 if yes)
I have a class called "BinarySearchTree" which has the following initialization function
def __init__(self, value=None):
# Initializes the tree with a value node, a left child and a right child
self.leftChild = None
self.rightChild = None
self.height = 1
self.value = value
I also have a stack class with the following "push" and "pop" functions:
def push(self, item):
# Adds an item to the beginning of the stack
ending = self.stack
self.stack = [item] + [ending]
def pop(self):
# Removes and returns the first element from the stack
if self.isEmpty():
return None
top_element = self.stack[0]
self.stack = self.stack[1:]
return top_element
I am trying to create a binary search tree instance from the lines in the text file and using the stack class. So far I have:
def loadTreeFromFile(filename):
binarySearchTree = stack.Stack()
with open(filename) as file:
# gets a list containing only the elements in the txt file
for level in file.readlines():
nodeInfo = level.rstrip().split()
data, lc, rc = int(nodeInfo[0]), int(nodeInfo[1]), int(nodeInfo[2])
print(data, lc, rc)
if rc == 1:
right_tree = binarySearchTree.pop()
if lc == 1:
left_tree = binarySearchTree.pop()
newTree = BinarySearchTree(data)
if rc == 1:
newTree.rightChild = right_tree
if lc == 1:
newTree.leftChild = left_tree
binarySearchTree.push(newTree)
return newTree
I am running into the problem when I try to display the BST, I get 8: [[[<__main__.BinarySearchTree object at 0x1033e4390>, []]], 9: [None, 10: [None, None]]] (I have a display function written for the BST class so this is not the problem) AND when I try to do anything with this newly created BST (such as get the depth, search it, etc), I get errors. Any help is much appreciated, thanks .

Inserting into n-child tree in Python

I am trying to implement a tree for the travelling salesperson problem. My particular tree has 5 destinations which are fully connected to each other.
One of the destinations is guaranteed to always be the starting destination and that you are only allowed to visit each destination once with the exception of the starting destination which you have to return to (ie if you have [1,2,3,4,5] with 1 the starting destination, a possible sequence of moves would be 1-3-5-2-4-1)
I tried implementing a tree in python with the following code (I brute forced it since I know the maximum depth is going to be 5).
class Node(object):
def __init__(self,value, city, children = [None, None, None, None]):
self.value = value
self.city = city
self.children = children
class Tree(object):
def __init__(self):
self.root = None
def insert(self,value,city):
newNode = Node(value,city)
if self.root is None:
self.root = newNode
else:
self._insert(1, newNode)
def _insert(self,depth, newNode):
if depth is 1:
for x in range(0,4):
if self.root.children[x] is None:
self.root.children[x] = newNode
return
elif self.root.children[3] is not None:
self._insert(2, newNode)
return
if depth is 2:
for x in range(0,4):
for y in range(0,3):
if self.root.children[x].children[y] is None:
self.root.children[x].children[y] = newNode
return
elif self.root.children[3].children[2] is not None:
self._insert(3, newNode)
return
if depth is 3:
for w in range(0,4):
for x in range(0,3):
for y in range(0,2):
if self.root.children[w].children[x].children[y] is None:
self.root.children[w].children[x].children[y] = newNode
return
elif self.root.children[3].children[2].children[1] is not None:
self._insert(4,newNode)
return
if depth is 4:
for w in range(0,4):
for x in range(0,3):
for y in range(0,2):
for z in range(0,1):
if self.root.children[w].children[x].children[y].children[z] is None:
self.root.children[w].children[x].children[y].children[z] = newNode
return
elif self.root.children[3].children[2].children[1].children[0] is not None:
self._insert(5,newNode)
return
if depth is 5:
for w in range(0,4):
for x in range(0,3):
for y in range(0,2):
for z in range(0,1):
for u in range(0,1):
if self.root.children[w].children[x].children[y].children[z].children[u] is None:
self.root.children[w].children[x].children[y].children[z].children[u] = newNode
return
elif self.root.children[3].children[2].children[1].children[0].children[0] is not None and w is 3 and x is 2 and y is 1 and z is 0 and u is 0:
print "The table is full"
def delete(self):
self.root = None
x = Tree()
x.insert(0, "Pretoria")
x.insert(60, "Johannesburg")
x.insert(1200, "Cape Town")
x.insert (600, "Durban")
x.insert(400, "Bloemfontein")
x.insert(1400, "Port Elizabeth")
My root and first level populate correctly but all the children nodes of the second, third, fourth and fifth level all populate exactly the same as the first level. When I checked their memory, they all populated the exact same memory space and I have no idea why. This happens when the following line of code runs:
x.insert(1400, "Port Elizabeth")
The tree for some reason is fully populated at this point despite only having 5 entries.
I tried using pointers at first but the same issue crops up.
Long story short, how would one go about inserting into an n-ary tree with decreasing n as you increase in depth?
This particular tree has the following attributes:
Root: 4 children per node (1 node with 4 children)
Level 1: 3 children per node (4 nodes with 3 children)
Level 2: 2 children per node (12 nodes with 2 children)
level 3: 1 child per node (24 nodes with 1 child)
level 4: 1 child per node (24 nodes with 1 child) (this is final destination in the TSP)

Categories