Pointers in Python on variables with None value - python

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

Related

Techniques used in checking if a binary tree is symmetric

Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
Question link is here
The recursion method need to traverse the tree twice.
But one of the comment provided a solution used a technique called 'Null check'. I can't understand why in this way can we avoid checking the tree twice?
Here is his code in C++:
bool isSymmetric(TreeNode* root) {
if (!root) return true;
return isSymmetric(root->left, root->right);
}
bool isSymmetric(TreeNode* t1, TreeNode* t2){
if (!t1 && !t2) return true;
if (!t1 || !t2) return false;
return t1->val == t2->val
&& isSymmetric(t1->left, t2->right)
&& isSymmetric(t1->right, t2->left);
}
I have also tried to modify it into python3 and my code also passed all test cases!
Here is my code:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetric(self, root):
return self.helper(root)
def helper(self,root):
if root is None:
return True
#why we can redefine the helper here?
def helper(left,right):
if left is None and right is None:
return True
if left is None or right is None:
return False
return left.val==right.val and helper(left.left,right.right) and helper(left.right,right.left)
return helper(root.left,root.right)
I have never met such kind of recursion before.
(1) Why we can redefine the function helper with different arguments in helper function itself?
(2) My intuition tells me that helper function will stop execution once it returns back to the root thus the tree won't be checked twice. But I don't know why.
A def statement is really just a fancy assignment statement. In Solution.helper, you are defining a local variable named helper that is bound to another function. As a result, all references inside Solution.helper and the local function to the name helper resolve to the local function.
Solution.helper is not a recursive function; only the local function is. You could write the same thing (less confusingly but equivalently) as
class Solution:
def isSymmetric(self, root):
return self.helper(root)
def helper(self,root):
if root is None:
return True
def helper2(left,right):
if left is None and right is None:
return True
if left is None or right is None:
return False
return left.val==right.val and helper2(left.left,right.right) and helper2(left.right,right.left)
return helper2(root.left,root.right)
The role of function isSymmetric(TreeNode* root is pretty simple. First, it returns true if the tree is empty, and if it's not, it checks if its left child is a mirror of its right child, which happens in the isSymmetric(TreeNode* t1, TreeNode* t2). So let's try to understand how the second function works. It is essentially designed to take two trees and check if they are mirrors of each other. How? First, it does the obvious checks. If one is null and the other is not, it returns false, and if both are null it returns true. The interesting part happens when both are potentially trees. It suffices that the left child of one is the mirror of the right child of the other and vice versa. You can draw a tree to see why this is the case. A schema should be self-explanatory.

Deleting node in BST (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".

Python Binary Search Tree with key and value

I need to implement a Binary Search Tree class as homework but I struggle making the insert function. I have looked through Google a lot to find some solutions or possibilities on how to do it but none of them has used a key and value (mostly just value) or if they used a key aswell, they had tons of seperate functions which I am not allowed to do I think.
So the pre-built is simply that:
class Node:
def __init__(self, key, value):
self.key = key
self.value = value
self.left = self.right = None
class BinarySearchTree:
def __init__(self):
self.root = None
self.size = 0
def __len__(self):
return self.size
def insert(self, key, value):
pass
def remove(self, key):
pass
def find(self, key):
pass
Now the thing is, if I want to check for example whether the value is smaller or bigger than a current Node to put it either right or left, I get Errors such as "root is not defined" or "root.right" has no such attribute etc...
And I guess that makes sense because self.root is declared as None.
But how do I actually fix it now to make the insert function work?
I am a little confused by this task as it uses key + value, so I need to insert the value bound to the specific key and in case the key already existed, overwrite its value.
its 5 in the morning so this might be all wrong, but here goes:
the key is what we are sorting by, the values aren't interesting
your insert function should probably look something like this:
def insert(self, key, value):
if self.root = None:
self.root = Node(key,value)
return
#regular binary tree traversal (comparing the key) to find where to insert, lets assume we need to insert on the left
parent.left = Node(key,value)
can you figure it out from here or would you like more direction
You didn't specify, but I'm guessing the point of the keys is to determine if a particular key is already in the tree, and if so, replace the related node's value in O(1) runtime complexity.
So when you're inserting a node, you will first check the dictionary for the key (you will initialize an empty dictionary yourself in __init__). If it already is there, then you simply just need to replace the value of the node for that particular key. Otherwise, you add the new node the same way that you would in any BST, and also remember to update your dictionary to map the key to it's node.

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

Concatenate Python Linked List

I am attempting to concatenate a Python linked list without copying the data contained within the nodes of the list. I have a function that will concatenate the list using copies of the nodes passed in, but I can't seem to get the function that doesn't use copies to work.
These functions are for testing and timing purposes; I know that Python's built-in list is awesome!
Here is the class I have been working with and the concatenate function.
class Cell:
def __init__( self, data, next = None ):
self.data = data
self.next = next
def print_list(self):
node = self
while node != None:
print node.data
node = node.next
The concatenation function is not meant to be a member function of the Cell class.
def list_concat(A, B):
while A.next != None:
A = A.next
A.next = B
return A
This function overwrites the first element of a list if the parameter A has more than one node. I understand why that is happening, but am not sure how to go about fixing it.
Here is the testing code I've been using for this function.
e = Cell(5)
test = Cell(3, Cell(4))
test2 = list_concat(test2, e)
test2.print_list()
Any insight or help would be greatly appreciated.
*edited to fix code formatting
Try this instead:
def list_concat(A, B):
current = A
while current.next != None:
current = current.next
current.next = B
return A
Assigning new values to a function's parameters is a bad programming practice, and the code in your question shows why: You used A for iterating over the original list, and by doing so, you lost the reference to its first element.
I'm not sure on about if extend performs a copy or not, but in case it doesn't, just use
A.extend(B)

Categories