Deleting node in BST (python) - python

Trying to build a binary search tree in python and came across this weird bug. After deleting nodes using my delete_node function, deleted nodes are still being printed, but only ones that are being deleted properly are nodes that have two other nodes attached to it (these ones are supposed to be hardest to delete though)
Here's the code:
class Node:
def __init__(self, data):
self.Left = self.Right = None
self.T_data = data
# function that deletes nodes from the tree
def delete_node(self, item):
if self is None:
return self
elif item < self.T_data:
self.Left.delete_node(item)
elif item > self.T_data:
self.Right.delete_node(item)
else:
# case when the node we want to delete has no leaves attached to it
if self.Right is None and self.Left is None:
self = None
# cases when a node has either left or right leaf node
elif self.Left is None:
temp = self.Right
self = None
return temp
elif self.Right is None:
temp = self.Left
self = None
return temp
else: #case when a node has two leaf nodes attached
temp = self.Right.min_node()
self.T_data = temp.T_data
self.Right = self.Right.delete_node(temp.T_data)
return self
As you can see the way nodes are deleted is using a recursion, so for double-branched nodes to get deleted, the single-branch node deletion should work properly, but it does not.
heres the print function and how the functions are called:
# function that prints contents of the tree in preorder fashion
def print_tree_preorder(self):
if self is None:
return
print("%s" % self.T_data)
if self.Left is not None:
self.Left.print_tree_preorder()
if self.Right is not None:
self.Right.print_tree_preorder()
x = int(input("Which element would you like to delete?\n"))
root = root.delete_node(x)
root.print_tree_preorder()

What you're doing right now, when you have:
self = None
Is not actually deleting the object itself. What you're doing is assigning self to a different value.
I think a good way to illustrate this problem is thinking of self and other variables as a tag.
When you say:
a = 3
You are essentially having the tag a put on the entity 3. 3 resides somewhere in memory, and a "points" to 3(although pointers in C++ isn't really the references in python, so be careful if you're going to make that comparison).
When you point self to None, what you wanted to say was:
So I want to remove this object, and all things that point to this object will point to None instead.
However, what you're currently saying is:
So I want to set my self tag to point to None.
Which is completely different. Just because you set your self tag to None does not mean you set the node's parents .Right or .Left members to None as well.
The solution? Well, you're not gonna like this, but you're gonna have to either:
have a pointer to the parent for each node, and set the parent's child(this child specifically) to None.
check 1 levels deeper in your tree, so you can delete the child node instead of deleting the node itself.
The reason the case for 2 node children works is because you're setting the attribute of the object here, instead of setting self=None. What this means is that you're still pointing to the same object here, specifically on this line:
self.T_data = temp.T_data
It's the difference between "Coloring a object does not make it a different object. Its traits are just different" vs. "replacing a object with another object makes it a different object".

Related

Python object isn't being created as normal even though I have created it as normal within the same function (doubly linked list)

I've created a doubly linked list and I'm having trouble with a function that swaps the first node with the last node, the second node with the second from last, etc. I'm creating various functions within the doubly linked list class (LDL). The problem comes with trying to create a node object. However it's strange as I have created the node objects without problem even within the same function. Here's the code:
class Node:
def __init__(self, data, nextLeft=None, nextRight=None):
self.data = data
self.nextLeft = nextLeft
self.nextRight = nextRight
class LDL:
def __init__(self, head=None):
self.head = head
def insertAtEnd(self, dataToInsert):
...
def reverseSwap(self):
nodeFirst = self.head # THE PROBLEM OBJECT
nodeCurrent = self.head
nodeLast = Node(None)
counter = 0
while nodeCurrent.nextRight is not None:
nodeCurrent = nodeCurrent.nextRight
conter += 1
for i in range(0, int(cont/2)):
temp = nodeFirst.data
nodeFirst.data = nodeLast.data # THE LINE OF THE ERROR
nodeLast.data = temp
nodeFirst = nodeFirst.nextRight
nodeLast = nodeLast.nextLeft
When I try to run this I get the error:
nodeFirst.data = nodeLast.data
AttributeError: 'NoneType' object has no attribute 'data'
However I knew this was coming as I can see while coding that it's not recognised as a Node object, because it doesn't offer to autofill nodeFirst.data or nodeFirst.nextLeft etc.
I have tried setting nodeFirst = nodeCurrent, I have tried placing it at various other parts of the code, I have tried different names, but I can't get it to work.
EDIT: I should point out that nodeCurrent = self.head works fine and as intended. This is also the first time I've come across this when working with linked lists as I have used a similar setup to work with singly linked lists with no problems.
If anybody could point me in the right direction that would be great.
because your init method of LDL class allows head to be None.
Therefore nodeFirst and nodeLast may become None.
to prevent this fix following
def __init__(self, head=None):
self.head=head
to this
def __init__(self, headdata):
self.head=Node(headdata)
or alternatively consider the edge case of list size = 0 in reverseSwap method

Python: Complicated iterables

I have seen this piece of code that iterates through certain members of a class if they exists. Notably, in a binary tree, iterating through the child until there are no more children.
Binary tree is defined as..
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
and they have iterated it like this:
# type root : TreeNode
def iterateTree(self, root):
level_list = [root]
while level_list:
for item in level_list:
print(item.val)
# This iterable seems really complicated for me to understand how they came up with this
level_list = [child for node in level_list for child in (node.left, node.right) if child]
I'm not sure how they came up with that line to iterate through the left and right node, I wouldn't have ever come up with that on the spot... How would I dissect this line?
Read as follows:
for node in level_list:
for child in (node.left, node.right):
if child:
child
if i'm not mistaken, this statement is a pythonic and short-hand way of creating a list.
# This iterable seems really complicated for me to understand how they came up with this
level_list = [child for node in level_list for child in (node.left, node.right) if child]
this is basically a shorthand way of doing the following set of lines:
for node in level_list:
for child in (node.left, node.right):
if child:
level_list.append(child)
the trick to understanding this shorthand statement is by looking at the peripheral bounding symbols, in this case these are [ and ]. which identifies with the list sequence in python. since there are iterators (for loops) in the list, we are basically creating or adding elements (variable child) in the said list.
/ogs

leaves_and_internals Function

This question is for school (homework) so I am not asking for code, and I don't want any, just an idea. I have to write a function that returns two lists, a list of the leaves and a list of the internal nodes of a binary tree. My algorithm is:
1) If both the left and the right subtrees are None, it is a leaf, and so I add it to the leaves list.
2) If they are not, then I add it to the internals list, and call the function on the left subtree, and then on the right, if they exist.
This is the code I have written:
def leaves_and_internals(self):
leaves = []
internals = []
if self.left is None and self.right is None:
leaves.append(self.item)
else:
internals.append(self.item)
if self.left != None:
leaves_and_internals(self.left)
else:
leaves_and_internals(self.right)
return internals, leaves
I'm pretty sure that the algorithm is correct, but I think that every time I recurse on the Nodes, the lists will get reset. How can I get around this?
Any help is greatly appreciated. Thanks
I have not looked into the algorithm of your code, and just merely suggesting an answer to the problem you're stuck at. You could pass leaves and internals as arguments to the recursive function, so that their contents get retained across the recursive calls.
In python, if you pass a mutable object to a function/method, the function/method gets a reference to the object. So as long as you still treat it as the same mutable object (i.e. not assign the parameter with something else directly), any changes you make to the object are also visible to the caller. Since list is a mutable type, this behavior is very much helpful for the case you're interested in.
And make sure to initialize the lists to [] before calling the leaves_and_internals function from outside.
def leaves_and_internals(self, leaves, internals):
if self.left is None and self.right is None:
leaves.append(self.item)
else:
internals.append(self.item)
if self.left != None:
leaves_and_internals(self.left, leaves, internals)
else:
leaves_and_internals(self.right, leaves, internals)
return
# Somewhere outside
leaves = []
internals = []
myobj.leaves_and_internals(leaves, internals)
UPDATE:
Since the OP mentions he cannot change the signature of the method nor use instance variables, this is an alternate solution I can think of which returns the leaves and internals to the caller. BTW, I assume some nodes in your tree can have both left and right, so you would need to check both (i.e. use 2 separate if instead of an if...else).
def leaves_and_internals(self):
leaves = []
internals = []
if self.left is None and self.right is None:
leaves = [ self.item ]
else:
if self.left != None:
leaves, internals = leaves_and_internals(self.left)
if self.right != None:
templeaves, tempinternals = leaves_and_internals(self.right)
leaves += templeaves
internals += tempinternals
internals.append(self.item)
return leaves, internals

Python basic data structure Implementation

I am a C++ coder. Recently started with Python. I was having a look at a simple Linked List implementation in Python. I am bit confused here. Not only here but also in Tree implementation and so on with the same problem.
class Element contains data and pointer to next node. Perfect no problem. However in class LinkedList I can see self.tail.next=e, now next is a variable of Element class even if it is public than also an object of Element class has to access it. Here how can we write something like self.tail.next = e as tail is just a variable of LinkedList class and is not an object of Element class. I am confused.
class Element:
def __init__(self,x):
self.data=x
self.next=None
class LinkedList:
def __init__(self):
self.head=None
self.tail=None
def append(self,x):
# create a new Element
e = Element(x)
# special case: list is empty
if self.head==None:
self.head=e
self.tail=e
else:
# keep head the same
self.tail.next=e
self.tail=e
Python works with references. Everything is always passed by reference, the values are always shared via references (unless explicitly copied).
Assigning an object means assigning the reference to that object. This way self.tail.next = e means: self.tail is expecting to refer to the object of the Element class. The object has the .next attribute. The self.tail.next = e means that the last element of the non-empty list is going to point to the just appended new element. Then the self.tail = e means that the reference to the last element is moved to the just appended last element.
Any variable in Python is just a reference variable with the given name. It is automatically dereferenced. Because of that it may look strangely to those familiar with classical compiled languages, like C++.
I am not sure if you can display the articles at Expert Exchange without creating the account. If yes, have a look at http://www.experts-exchange.com/Programming/Languages/Scripting/Python/A_6589-Python-basics-illustrated-part-2.html and namely the http://www.experts-exchange.com/Programming/Languages/Scripting/Python/A_7109-Python-basics-illustrated-part-3.html for the images that explain the problem.
The only place where you initialize self.tail is in append and in there you set it to be equal to e. Just a little bit above you have e = Element(x) and so e is an object of type Element. Please note that in the moment you call self.tail.next=e you know head is not none and thus tail is also not None but an instance of Element.
Hope this helps.
You wrote:
now next is a variable of Element class even if it is public than also an object of Element class has to access it.
There is a misunderstanding there. Public members (i.e. all members except those that start with two underscores) are accessible from anywhere, not only from within methods of the same class.
next and tail are Elements, that's why. This would be true for a linked list in any language, including C or C++. A linked list is a list of elements linked together with pointers/references.
When the list is traversed, those links are used to get from node (element) to node. It would not make sense if they referred to other linked lists, would it? The first element in the list is the head, the last element is the tail, the next element pointed to in a node is the element after it in the list, the previous node is the element before it.
In python everything is a reference.
Please: separate the list implementation from your (application) data.
As in C++ it is good style to encapsulate the access methods - nevertheless because there are no access restrictions to instance fields they can be accessed from everywhere.
An idea of a generic double linked list (this is only a skeleton which should be enhanced - but I hope it transports the idea).
class DoubleLinkedList(object):
class ListNode(object):
def __init__(self):
self.__next = None
self.__prev = None
def next(self):
return self.__next
def prev(self):
return self.__prev
def set_next(self, next_node):
self.__next = next_node
def set_prev(self, prev_node):
self.__prev = prev_node
def is_last(self):
return self.next()==None
def __init__(self):
'''Sets up an empty linked list.'''
self.__head = DoubleLinkedList.ListNode()
self.__tail = DoubleLinkedList.ListNode()
self.__head.set_next(self.__tail)
self.__tail.set_prev(self.__head)
def first(self):
return self.__head.next()
def last(self):
return self.__tail.prev()
def append(self, list_node):
list_node.set_next(self.__tail)
list_node.set_prev(self.__tail.prev())
self.__tail.set_prev(list_node)
list_node.prev().set_next(list_node)
########################################
class MyData(DoubleLinkedList.ListNode):
def __init__(self, d):
DoubleLinkedList.ListNode.__init__(self)
self.__data = d
def get_data(self):
return self.__data
ll = DoubleLinkedList()
md1 = MyData(1)
md2 = MyData(2)
md3 = MyData(3)
ll.append(md1)
ll.append(md2)
ll.append(md3)
node = ll.first()
while not node.is_last():
print("Data [%s]" % node.get_data())
node = node.next()

Pointers in Python on variables with None value

I have a method that creates a new node in a tree - either left or right. If the value is lower than my current value it is inserted on the left, otherwise on the right side.
I want to refactor this code, so that I first see on which side I have to insert my element, and then insert it. Before I implemented this twice: Once for the left side and once for the right side.
It currently looks like this:
def neu(self, sortByValue, secondValue):
child = self.left if(sortByValue.lower() < self.value[0].lower()) else self.right
if(child == None):
child = MyTree(sortByValue,secondValue)
else: child.neu(sortByValue,secondValue)
My problem is, though, that self.left is None, and self.right is None. So when I create child as a variable and set it to MyTree(...), self.left and self.right are not receiving the value.
Is there anything I can do to improve this? Thanks!
In Python variables are names not locations. For example:
>>> a = 1
>>> b = a
>>> a = 2
>>> print b
1
In your code you're simply rebinding the name child to a different value (your new node) and that has no affect on the previously bound value (None).
Here's a reworking of your code that should do what you want (untested):
def neu(self, sortByValue, secondValue):
def child(node):
if(node is None):
return MyTree(sortByValue, secondValue)
else:
child.neu(sortByValue, secondValue)
return node
if(sortByValue.lower() < self.value[0].lower()):
self.left = child(self.left)
else:
self.right = child(self.right)
Hallo ;-)
self.left or self.right don't receive the value because you assign to child which just holds a copy of the destination value and no reference to it.
You want to have a pointer- This doesn't exist directly in Python.
You could express this using a class wrapper, but I think it's more comprehensible when you just write both possibilities in the if clause.
Why are you using a tree?
I would use a dictionary:
Initialization:
tree = {}
Adding new node:
tree[sortByValue] = secondValue
Extracting things
print tree[sortByValue]
Not the straight answer but a more pythonic way of doing it

Categories