How to do tree traversal? - python

So I am running into some issue that has left me dumbfounded. I do not understand where I am going wrong with my code but the idea I have is I check if the current node I am at is None and if it is then I return a list of my tree in in order, pre order, and post order. Here is my code:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def inOrder(self, arr=[]):
if self is not None:
self.left.inOrder(arr)
arr.append(self.data)
self.right.inOrder(arr)
return arr
When I run it I get an error of self.left.inOrder() AttributeError: 'NoneType' object has no attribute 'inOrder' which I have not idea as to why. I am checking that self is not None so shouldn't this guarantee my Node to have a left and right.
I am only showing the inOrder I have implemented.
I have solved this instead by doing the following
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def inOrder(self):
if self is not None and self.left is not None:
self.left.inOrder()
print(self.data)
if self is not None and self.right is not None:
self.right.inOrder()
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.inOrder()
Whether I save it to a list or just print it is fine with me but if I already check if self is not None then shouldn't I be able to call self.left.inOrder and self.right.inOrder?

self is not None. self refers to an instance of the class. self.left is an instance attribute that you set to None in your __init__() method when you create the instance. Because self.left refers to None and the object None does not have the inOrder() method, you obtain an AttributeError. Within your class definition, self (referring to an instance of the class) does have the inOrder() method, but it's attribute self.left (referring to None), does not.

Related

Problem understanding Linked Lists in Python

I'm learning about data structures and algorithms and I'm starting to learn about constructing linked lists from scratch in python. Right now I understand how they work and the components that go into making them (Nodes, data/address, Head/Tail, etc), but I'm having a really hard time wrapping my brain around how they function when constructing them in python. Like I have working code to make them in python here but I don't get the logic behind how they operate with classes. For example, I'm confused in my addLast-function on how the node variable(node = Node(value)) connects to the Node class.
class Node:
def __init__(self, value, next=None):
self.value = value
self.next = next
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
def addLast(self, value):
node = Node(value)
if self.head == None:
self.head = node
self.tail = node
else:
self.tail.next = node
self.tail = node
class Node:
def __init__(self, value, next=None):
self.value = value
# NODE POINTS TO THE NEXT ELEMENT IF PROVIDED ELSE NONE
self.next = next
class LinkedList:
def __init__(self):
# INIT AN EMPTY LINKED LIST
self.head = None
self.tail = None
def addLast(self, value):
# CREATE A NODE OBJECT WITH VALUE 'value' WHICH POINTS TO NOTHING (because it's the end of the linked list)
node = Node(value)
# IF NO HEAD IT BECOMES THE HEAD AND THE TAIL
if self.head == None:
self.head = node
self.tail = node
else:
# ADD THE NODE TO THE END (tail) OF THE LINKED LIST
self.tail.next = node
self.tail = node
# Create an empty linked_list
head = Linked_list()
# Add at the end a node with the value 1
head.addLast(1)
Hope it's clearer for you, ask questions if needed
I think this may help you understand what the code actually do hedind the scenes.
You can paste the following code to the link and click the "Visualize Execution" button. It will visualize all details step by step.
Good Luck!
class Node:
def __init__(self, value, next=None):
self.value = value
self.next = next
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
def addLast(self, value):
node = Node(value)
if self.head == None:
self.head = node
self.tail = node
else:
self.tail.next = node
self.tail = node
head = LinkedList()
head.addLast(1)
head.addLast(2)

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.

Can you check if two objects instances are same

In a LinkedList Class defined here, I wanted to check if you can check self.head == node or you need to compare the node with all the attributes and define a equals method explicitly? I saw code where someone was using this without equals method
class Node(object):
def __init__(self,key=None,value=None):
self.key = key
self.value = value
self.previous = None
self.next = None
class LinkedList(object):
def __init__(self):
self.head = None
self.tail = None
self.count = 0
def prepend(self,value):
node = Node(value)
if self.head is None:
self.head = node
self.tail = self.head
self.count = 1
return
self.head.previous = node
node.next = self.head
self.head = node
self.count += 1
To see if the address of obj1 matches the address of obj2, use the is operator.
You are doing that already in this test:
if self.head is None:
There is exactly one object (a singleton) in the NoneType class,
and you are essentially asking if id(self.head) matches the id(), or address, of None.
Feel free to do that with other linked list node objects.
If, OTOH, you were to ask if self.head == some_node,
that might well be asking if node attribute a matches in both,
and attribute b matches in both,
depending on your class methods,
e.g. using def __eq__.
A node created by a shallow copy might be == equal to original,
but is will reveal that separate storage is allocated for it.

Python: How to check if an input is an instance of a class within that class?

I tried to write a code that protects the pointer of a linked list. The setter should point only to an instance which belongs to the same class. Usually isinstance() or type() work after the class is defined. But the setter is a method inside that class, hence the class is not fully defined yet.
I have no doubt that type() gives out error. But I wonder why isinstance(instance, class) yields False if calling from another class.
#define the Node class
class Node:
"""Class Node contain only data and a next pointer variables."""
def __init__(self, Data = None):
self.data = Data
self.__next = None
def getNext(self):
"""The getter"""
return self.__next
def setNext(self, NextNode):
"""The setter"""
#if possible check if NewNode is an instance of Node before pointing to it.
#I have tried "isinstance(), type(), etc. but failed.
if isinstance(NextNode, Node):
self.__next = NextNode
else:
print('The Next pointer should point to "Node" only.')
Then check if isinstance is working
ANode = Node((1,2,3))
BNode = Node((5,6))
ANode.setNext(BNode)
print(BNode)
print(ANode.getNext())
Both prints yield the same address
<__main__.Node object at 0x112162828>
<__main__.Node object at 0x112162828>
So everything looks fine. But When I call from the LinkedList class, printed below, the isinstance yields False, as seen from my warning.
class LinkedList:
"""This class is the Linked List of Node."""
def __init__(self, FirstNode = None):
"""Initialize by creating an empty List. __First hold the pointer that point to the first node."""
if FirstNode is None:
self.__first = Node(None)
self.__last = self.__first
elif type(FirstNode) is Node:
self.__first = FirstNode
self.__last = self.__first
else:
print('To create a linked-list enter nothing or a Node.')
raise TypeError
def getFirst(self):
return self.__first
def append(self, NewLastNode):
"""Add LastNode to the end of the list."""
if not isinstance(NewLastNode,Node):
raise TypeError
OldLast = self.__last
OldLast.setNext(NewLastNode)
self.__last = NewLastNode
NewLastNode.setNext(None)
def removeFirstNode(self):
"""Remove the first node (when the buffer is full)."""
OldFirst = self.__first
NewFirst = OldFirst.getNext()
if NewFirst == None:
# just clear data
OldFirst.data = None
else:
self.__first = NewFirst
del OldFirst
Then I create an instance of the LinkedList class
LL = LinkedList(Node((1,2)))
NewNode = Node((2.0, 3.0, -10))
Surely isinstance works fine here
isinstance(NewNode,Node)
yields True, but
LL.append(NewNode)
which will call Node.setNext() and there the isinstance() yields False as the else in Node.setNext() prints out
The Next pointer should point to "Node" only.
The piece of code that's giving you the error is this:
NewLastNode.setNext(None)
because you're trying to set the next element to an object that it's not a Node instance, hence the error.
I think you could simply remove this statement, as your self.__last is now correctly pointing to your NewLastNode. So your code will become:
def append(self, NewLastNode):
"""Add LastNode to the end of the list."""
if not isinstance(NewLastNode,Node):
raise TypeError
OldLast = self.__last
OldLast.setNext(NewLastNode)
self.__last = NewLastNode

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