Calculating no. of nodes in a Binary Tree in O(1)? - python

I am taking Data Structures and Algorithm course from Jovian. Currently on Binary Tress, but I am stuck on one question where we need to calculate no. of nodes in O(1) time.
Firstly here's how the final class TreeMap looks like:
class TreeMap:
def __init__(self):
self.root = None
def __setitem__(self, key, value):
node = find(self.root, key)
if not node:
self.root = insert(self.root, key, value)
self.root = balance_tree(self.root)
else:
update(self.root, key, value)
def __getitem__(self, key):
node = find(self.root, key)
return node.value if node else None
def __iter__(self):
return (x for x in list_all(self.root))
def __len__(self):
return size(self.root)
def display(self):
return display_keys(self.root):
Currently, it's calculating no. of nodes with the recursion method. I think we just need a counter and increment every time a node is created and we also have a hint to modify the BSTNode class. So this is how I did it:
class BSTNode:
counter = 0
def __init__(self, key, value=None):
self.key = key
self.value = value
self.left = None
self.right = None
self.parent = None
BSTNode.counter += 1
But I don't know how do I implement or use the counter in __len__ function in class TreeMap. Any help would be much appreciated.
Here is the link to Jovian Lesson: https://jovian.ai/learn/data-structures-and-algorithms-in-python/lesson/lesson-2-binary-search-trees-traversals-and-balancing

Related

Python 3 - Using recursion in a linked list

Python 3 - I am new to coding and am finding recursion difficult. I'm making a linked list class with recursive methods for adding and removing items from the list. Right now, I am unable to remove an item if it happens to be the first item in my list. I wrote some alternative code which could remove the first item from the list if I included another parameter (previous) and another base case, but then I could only remove the first item and spent way too long trying to figure out why so I scrapped that entirely. I would appreciate a hint!
Also, I am already aware that I have getters and am not using them properly.
class Node:
"""
Represents a node in a linked list
"""
def __init__(self, data):
self._data = data
self._next = None
def get_data(self):
"""getter method for data in Node class"""
return self._data
def get_next(self):
"""getter method for next in Node class"""
return self._next
class LinkedList:
"""
A linked list implementation of the List ADT
"""
def __init__(self):
self._head = None
def get_head(self):
"""getter function for head of list"""
return self._head
def add(self, val):
""" Adds a node containing val to the linked list - helper function"""
self._head = self.recursive_add(self._head, val)
def recursive_add(self, node1, val):
""" Adds a node containing val to the linked list """
if node1 is None:
return Node(val)
else:
node1._next = self.recursive_add(node1._next, val)
return node1
def remove(self, val):
"""removed the node containing val from the linked list - helper function"""
self.recursive_remove(self._head, val)
def recursive_remove(self, node1, val):
"""
Removes the node containing val from the linked list
"""
if node1 is None:
return node1
elif node1._data == val:
return node1._next
else:
node1._next = self.recursive_remove(node1._next, val)
return node1
def main():
my_list = LinkedList()
my_list.add(13)
my_list.add(9)
my_list.add(5)
my_list.remove(9)
if __name__ == '__main__':
main()
def remove(self, val):
"""removed the node containing val from the linked list - helper function"""
if self._head and self._head._data == val:
self._head = self._head._next
return
self.recursive_remove(self._head, val)
if its at the start, the head needs to be changed.
In remove you call recursive_remove, but ignore its return value. You should use it as the (potentially different) _head reference, must like is done in the recursive method itself, where you have:
node1._next = self.recursive_remove(node1._next, val)
# ^ │
# └───────────────────────────────────┘
Note how node1._next is passed as argument, and the method's return value is the (potentially different) reference that node1._next should end up with. The same pattern should be applied in the initial call in remove:
def remove(self, val):
self._head = self.recursive_remove(self._head, val)
# ^ │
# └──────────────────────────────────┘
NB: the same pattern is used in add, where you do it correctly.

Understanding variable share between recursion

I was doing this leetcode question:(https://leetcode.com/problems/binary-tree-inorder-traversal/) in which I came up with this solution:
# 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 inorderTraversal(self, root: TreeNode) -> List[int]:
if root is None:
return None
result = []
if root.left is None and root.right is None:
result.append(root.val)
return result
return self.traverse(root,result)
def traverse(self,node,result):
if node is None:
return result
result = self.traverse(node.left,result)
result.append(node.val)
result = self.traverse(node.right,result)
return result
However I found out I actually don't need to store the results of recursion call in the variable and I can simply do this:
# 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 inorderTraversal(self, root: TreeNode) -> List[int]:
if root is None:
return None
result = []
if root.left is None and root.right is None:
result.append(root.val)
return result
return self.traverse(root,result)
def traverse(self,node,result):
if node is None:
return result
self.traverse(node.left,result)
result.append(node.val)
self.traverse(node.right,result)
return result
My understanding was that in each recursion call, we are passing a reference to the result variable, not copying the result variable, so what is happening is that when the recursion call gets to the left most node, it appends the value and returns to its parent node, and since we had pass by reference, the result variable in parent node already has the leftmost node added to it, so it just adds the parent node to it and keep continuing on the recursion.
Is my understanding correct or is there something else going on?
Thanks
Yes, your understanding is right.
Note: you are sharing the same code in both boxes.

tree.put(key, value) insertion

So I'm working on an assignment for a data structures class, and one question is to create a put(key, value) method for an AVLTree. I know I have to adding the balancing method, but right now I'm just on the actual insertion. I want the function to work where you can create a tree and type newTree.put(key value) and have it work. Right now I have
class node:
def __init__(self, key, value):
self.key = key
self.value = value
self.left = None
self.right = None
self.height = 0
class AVLTreeMap:
def __init__(self, key, value):
self.root = node(key, value)
#inserting new key-value pair, NEED TO ADD BALANCING ABILITY
def put(self, key, value, height=0):
if(key < self.root.key):
if(self.root.left == None):
self.root.left = node(key, value)
self.root.left.height = height+1
else:
self.root = self.root.left
self.put(key, value, height+1)
else:
if(self.root.right == None):
self.root.right = node(key, value)
self.root.right.height = height+1
else:
self.root = self.root.right
self.put(key, value, height+1)
However, the recursive aspect of put just discounts the root, and creates a new tree of just one parent and that node as one child. Is this the right way to go about this, or is there an easier way? Also, if I do it this way, how do you recurse left and right in this method?

Python Linked List with Nodes. Iterable

I need some help writing an __iter__() method for my UnorderedList() class. I tried this:
def __iter__(self):
current = self
while current != None:
yield current
But the while loop doesn't stop. Here is the rest of my classes and code:
class Node:
def __init__(self,initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self,newdata):
self.data = newdata
def setNext(self,newnext):
self.next = newnext
class UnorderedList:
def __init__(self):
self.head = None
self.count = 0
If you want to iterate all items succeedingly, you should do
def __iter__(self):
# Remember, self is our UnorderedList.
# In order to get to the first Node, we must do
current = self.head
# and then, until we have reached the end:
while current is not None:
yield current
# in order to get from one Node to the next one:
current = current.next
so that in every step you go one step further.
BTW, setters and getters aren't used in Python in the form of methods. If you need them, use properties, otherwise omit them altogether.
So just do
class Node(object):
def __init__(self, initdata):
self.data = initdata
self.next = None
class UnorderedList(object):
def __init__(self):
self.head = None
self.count = 0
def __iter__(self):
current = self.head
while current is not None:
yield current
current = current.next

Python object instantiation

I am very new to python and need some help with instantiating an object. The python interpreter is giving me trouble when instantiating an object of a class I defined. There are two classes, BTNode and BST (which are stored in files bst_node.py and bst.py respectively):
# file: bst_node.py
class BTNode:
"""a binary search tree node implementation"""
def ___init___(self, value):
self.value = value
self.left is None
self.right is None
self.parent is None
def ___init___(self, value, left, right, parent):
"""set the parameters to corresponding class members"""
self.value = value
self.left = left
self.right = right
self.parent = parent
def is_leaf(self):
"""check whether this node is a leaf"""
if self.left.value is None and self.right.value is None:
return True
return False
# file: bst.py
from bst_node import *
class BST:
"""a binary search tree implementation"""
def ___init___(self, value):
self.root = BTNode(value)
def insert(self, curRoot, newValue):
if curRoot.is_leaf():
if newValue < curRoot.value:
newNode = BTNode(newValue, None, None, curRoot)
curRoot.left = newNode
else:
newNode = BTNode(newValue, None, None, curRoot)
curRoot.right = newNode
else:
if newValue < curRoot.value:
self.insert(curRoot.left, newValue)
else:
self.insert(curRoot.right, newValue)
So, in the interpreter I do:
import bst as b
t1 = b.BST(8)
and I get an error which says that this constructor takes no arguments
The constructor clearly takes an argument value so what is going wrong here? How can I fix this error?
Thanks, all help is greatly appreciated!
The first issue is that you called your functions ___init___ instead of __init__. All of the 'special methods' use two underscores.
A second issue in this code is that in BTNode you redefined __init__. You can't overload functions in python. When you reclare __init__ you effectively deleted the first constructor.
A third issue is your usage of is. is is an operator that checks whether two objects are exactly the same and returns True or False. In the constructor, you have a few self.left is None is examining the value of self.left (which wasn't declared yet), and examining whether or not it is None. To set it, use = as follows:self.left = None
To fix the second and third issue you should use default argument values. For example:
def __init__(self, value, left=None, right=None, parent=None):
In addition to the number of underscores problem, you should replace
def ___init___(self, value):
self.value = value
self.left is None
self.right is None
self.parent is None
def ___init___(self, value, left, right, parent):
"""set the parameters to corresponding class members"""
self.value = value
self.left = left
self.right = right
self.parent = parent
with
def __init__(self, value, left=None, right=None, parent=None):
"""set the parameters to corresponding class members"""
self.value = value
self.left = left
self.right = right
self.parent = parent
Because as #Moshe points out, you can't overload functions, you should use default arguments insted.
Changing ___init___ to __init__ should fix it. (2 underscores vs 3)

Categories