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

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.

Related

maximum heap priority queue python

For school i have to make an assignment -->
"The city of Amsterdam wants to store the maximum values of the past few years for research
purposes. It is important that the current maximum measured value can be accessed very quickly.
One idea to fulfill this requirement is to use a priority queue. Your job is to implement a priority
queue with a maximum heap and return again a tuple of the current maximal measurement and
its corresponding date when the maximum occurred. Output: date,covid level"
The program takes as input:
(string)’yyyy-mm-dd’, (int)sensor id, (int)covid level.
The expected output is: yyyy-mm-dd,covid level.
Input: 2022−09−08, 23, 371; 2022−09−08, 2, 3171; 2022−09−08, 12, 43; 2021−03−21, 4, 129
Output: 2022 −09 −08, 3171
I have provided my code below. When creating a max heap, the max element should be the first element (root). A Max-Heap is a complete binary tree in which the value in each internal node is greater than or equal to the values in the children of that node, though when inserting the tuples the nodes do not get sorted. My output is very strange, i do not understand where it comes from. When putting in the above input, this is my output:
1.1.1977, 9223372036854775807
could somebody help me? what piece of code am i missing, i have gone over it so many times.
import sys
class MaxHeap:
def __init__(self, maxsize):
self.maxsize = maxsize
self.size = 0
self.Heap = [0] * (self.maxsize + 1)
self.Heap[0] = ('1.1.1977', sys.maxsize)
self.FRONT = 1
# Function to return the position of
# parent for the node currently
# at pos
def parent(self, pos):
return pos // 2
# Function to return the position of
# the left child for the node currently
# at pos
def leftChild(self, pos):
return 2 * pos
# Function to return the position of
# the right child for the node currently
# at pos
def rightChild(self, pos):
return (2 * pos) + 1
# Function that returns true if the passed
# node is a leaf node
def isLeaf(self, pos):
if pos >= (self.size // 2) and pos <= self.size:
return True
return False
# Function to swap two nodes of the heap
def swap(self, fpos, spos):
self.Heap[fpos], self.Heap[spos] = (self.Heap[spos],
self.Heap[fpos])
# Function to heapify the node at pos
def maxHeapify(self, pos):
if not self.isLeaf(pos):
if (self.Heap[pos] < self.Heap[self.leftChild(pos)] or
self.Heap[pos] < self.Heap[self.rightChild(pos)]):
if (self.Heap[self.leftChild(pos)] >
self.Heap[self.rightChild(pos)]):
self.swap(pos, self.leftChild(pos))
self.maxHeapify(self.leftChild(pos))
else:
self.swap(pos, self.rightChild(pos))
self.maxHeapify(self.rightChild(pos))
# Function to insert a node into the heap
def insert(self, element):
if self.size >= self.maxsize:
return
self.size += 1
self.Heap[self.size] = element
current = self.size
while (self.Heap[current] >
self.Heap[self.parent(current)]):
self.swap(current, self.parent(current))
current = self.parent(current)
# Function to print the contents of the heap
def Print(self):
for i in range(1, (self.size // 2) + 1):
print(i)
print("PARENT : " + str(self.Heap[i]) +
"LEFT CHILD : " + str(self.Heap[2 * i]) +
"RIGHT CHILD : " + str(self.Heap[2 * i + 1]))
# Function to remove and return the maximum
# element from the heap
def extractMax(self):
extraction = self.Heap[self.FRONT]
self.Heap[self.FRONT] = self.Heap[self.size]
self.size -= 1
self.maxHeapify(self.FRONT)
return extraction
# Driver Code
if __name__ == "__main__":
input = input()
input = input.split(";")
dates = []
values = []
for d in input:
date = d.split(',', 2)
dates.append(date[0])
values.append(date[2])
values = [int(x) for x in values]
tuples = list(zip(dates, values))
heap = MaxHeap(len(tuples) + 1)
# print(tuples)
for t in tuples:
heap.insert(t)
print(t)
print(heap.extractMax())

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

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

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

Binary search tree cumsum

Problem: Given a binary search tree in which the keys are numbers, we'll define the operation 'cumsum' ( shorthand for cumulative sum ) that switches the key of every node in the tree with the sum of all the keys that are smaller or equal to it.
For example,
In this example,
The key 5 in the root is switched to the value 10: the sum of the original key in the root ( which is 5 ) and all smaller keys than it ( which are 2 and 3 ).
The key 3 is switched with the value 5: sum of the original key in this node ( meaning, 3 ) and all the smaller keys than it ( which is 2 ).
The key 12 in the rightmost node is switched with the value 45: sum of the original key in this node ( meaning, 12 ) and all the smaller keys than it ( which are 2,3,5,6,8 and 9 ).
Note that the method needs to be an envelope function that envelopes a recursive function. Also note that the method cumsum does not return a new tree but rather updates the tree itself ( in-place )
My attempt:
def cumsum(T):
def cumsum_rec(node,L):
L.append(node.key)
if node.left != None:
cumsum_rec(node.left,L)
if node.right != None:
cumsum_rec(node.right,L)
count = 0
for val in L:
if val < node.key:
count += val
node.key += count
L = []
cumsum_rec(T.root,L)
Explanation: I traverse each node in the tree, I save each original node in an auxiliary list denoted as 'L'. When all nodes were traversed, I look for all the nodes keys in the list 'L' that are smaller than the current node and appropriately update the key of the current node to be the sum of the keys of all nodes whose key are smaller or equal to the current node's key .
My implemenation works, for example, for the tree in the example above:
t = Binary_search_tree()
t.insert(5,'A')
t.insert(2,'A')
t.insert(9,'A')
t.insert(3,'A')
t.insert(8,'A')
t.insert(12,'A')
t.insert(6,'A')
which looks:
>>> print(t)
5
______/ |__________
2 9
/ |__ __/ |__
# 3 8 12
/ | __/ | / |
# # 6 # # #
/ |
# #
And after performing cumsum operation on it:
>>> cumsum(t)
>>> print(t)
10
______/ |____________
2 33
/ |__ __/ |__
# 5 24 45
/ | __/ | / |
# # 16 # # #
/ |
# #
My question:
Although my implementation works, I was interested in seeing other possible implementations for the sake of learning. Do you have an alternative implementation? one that doesn't require using a list as an input to the recursive function?
Addendum ( implementation of Binary search tree and Tree_node classes if you're interested ):
def printree(t, bykey = True):
"""Print a textual representation of t
bykey=True: show keys instead of values"""
#for row in trepr(t, bykey):
# print(row)
return trepr(t, bykey)
def trepr(t, bykey = False):
"""Return a list of textual representations of the levels in t
bykey=True: show keys instead of values"""
if t==None:
return ["#"]
thistr = str(t.key) if bykey else str(t.val)
return conc(trepr(t.left,bykey), thistr, trepr(t.right,bykey))
def conc(left,root,right):
"""Return a concatenation of textual represantations of
a root node, its left node, and its right node
root is a string, and left and right are lists of strings"""
lwid = len(left[-1])
rwid = len(right[-1])
rootwid = len(root)
result = [(lwid+1)*" " + root + (rwid+1)*" "]
ls = leftspace(left[0])
rs = rightspace(right[0])
result.append(ls*" " + (lwid-ls)*"_" + "/" + rootwid*" " + "|" + rs*"_" + (rwid-rs)*" ")
for i in range(max(len(left),len(right))):
row = ""
if i<len(left):
row += left[i]
else:
row += lwid*" "
row += (rootwid+2)*" "
if i<len(right):
row += right[i]
else:
row += rwid*" "
result.append(row)
return result
def leftspace(row):
"""helper for conc"""
#row is the first row of a left node
#returns the index of where the second whitespace starts
i = len(row)-1
while row[i]==" ":
i-=1
return i+1
def rightspace(row):
"""helper for conc"""
#row is the first row of a right node
#returns the index of where the first whitespace ends
i = 0
while row[i]==" ":
i+=1
return i
#######################################################################
class Tree_node():
def __init__(self, key, val):
self.key = key
self.val = val
self.left = None
self.right = None
def __repr__(self):
return "(" + str(self.key) + ":" + str(self.val) + ")"
class Binary_search_tree():
def __init__(self):
self.root = None
def __repr__(self): #no need to understand the implementation of this one
out = ""
for row in printree(self.root): #need printree.py file
out = out + row + "\n"
return out
def lookup(self, key):
''' return node with key, uses recursion '''
def lookup_rec(node, key):
if node == None:
return None
elif key == node.key:
return node
elif key < node.key:
return lookup_rec(node.left, key)
else:
return lookup_rec(node.right, key)
return lookup_rec(self.root, key)
def insert(self, key, val):
''' insert node with key,val into tree, uses recursion '''
def insert_rec(node, key, val):
if key == node.key:
node.val = val # update the val for this key
elif key < node.key:
if node.left == None:
node.left = Tree_node(key, val)
else:
insert_rec(node.left, key, val)
else: #key > node.key:
if node.right == None:
node.right = Tree_node(key, val)
else:
insert_rec(node.right, key, val)
return
if self.root == None: #empty tree
self.root = Tree_node(key, val)
else:
insert_rec(self.root, key, val)
Thanks in advance for any help!
Here's one implementation that doesn't require keeping the extra list; it just adds the numbers up as it goes.
def cumsum(T):
def cumsum_rec(node, initial):
if node is None:
return initial
left = cumsum_rec(node.left, initial)
node.key = left + node.key
right = cumsum_rec(node.right, node.key)
return right
cumsum_rec(T.root, 0)
Note that there is no need to do extra comparisons of values (my code has no <), because all of that information is already contained in the structure of the tree.

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 .

Categories