Python: Complicated iterables - python

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

Related

Can't figure out how to stop returning list inside of list (leetcode problem)

Returning list inside of list (not desired)
I keep returning a list inside of a list for the following LeetCode Problem 257. Binary Tree Paths, which is not what the problem wants. I always seem to run into this issue while solving traversal problems.
Here is my current solution along with it's output.
# 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 binaryTreePaths(self, root: TreeNode, val: str="") -> List[str]:
if not root:
return ""
val += str(root.val) + "->"
if not root.left and not root.right:
return val[:-2]
return [path for path in [self.binaryTreePaths(root.left, val), self.binaryTreePaths(root.right, val)] if path]
Input: [1,2,3,null,5]
Output: [["1->2->5"],"1->3"]
Expected: ["1->2->5","1->3"]
Things I've tried
Creating a res variable and using res.extend()
res = []
res.extend(path for path in [self.binaryTreePaths(root.left, val), self.binaryTreePaths(root.right, val)] if path)
Using append instead of extend along with conditionals to try and filter out unwanted elements.
I constantly run into this problem while solving traversal problems so if anyone here has a solution along with some general advice on how to develop an intuition on what's going wrong here and how to solve it in the future, I'd be extremely grateful for your help.
Edit
Well I kept working at trying to fix my solution and after about ~20 minutes, I came up with the following abomination.
class Solution:
def binaryTreePaths(self, root: TreeNode, val: str="") -> List[str]:
if not root:
return ""
val += str(root.val) + "->"
if not root.left and not root.right:
return val[:-2]
r1 = self.binaryTreePaths(root.left, val)
r2 = self.binaryTreePaths(root.right, val)
if isinstance(r1, str):
r1 = [r1]
if isinstance(r2, str):
r2 = [r2]
for i in r1:
if i == "":
r1.pop(r1.index(i))
for i in r2:
if i == "":
r2.pop(r2.index(i))
return r1 + r2
I'm by no means satisfied with my solution as is so if you know of a cleaner fix please continue to post your fix/advice.
The root issue is that the binaryTreePaths function returns different types depending on the conditions. If root is falsy or when there is no left or right set, it returns just a string ("" or val[:-2]), but in all other cases it returns a list.
The list is only needed for the first level of regression (when returning the final value back to the caller), but the way it is setup it can also return a list from any level of recursion. So, any recursion that also meets the criteria to return the list comprehension will end up with a list within a list at the lower levels of recursion.
So, when you travel more than one node, it will be encased in a list for each additional node. Thus, "1->3" isn't in a list because it was immediately returned as a string after the first recursion on the right side, but "1->2->5" was in a list because there was one extra level of recursion before the string was returned.
There are multiple ways to resolve this - you could track the level of regression and only respond with left/right strings at any depth above 1, then return the list only from level 1, for example. Or you could have a parent function that handles the first layer and a helper function (perhaps a child function within it) that does the recursion part.
Half the fun is figuring it out - so I leave the code piece to you. Hopefully this gave you to tools to resolve it yourself (per the second half of your question). The key is to construct at each recursion level exactly what the return value is. The best way to do this in real-time is to run your code in debugging mode, putting a breakpoint at the start of the recursive function, and follow the code through, and look at what the actual arguments and return values are as it recurses. Do this a few times and you will start to be able to think your way through what is happening without the debugging.
(edit: another trick you can use is writing out the list comprehension long-form - with for loops and if/else statements. It's not as compact, but usually makes it easier to understand which values get assigned when.)
For this problem, we can use stack. This'd get accepted:
class Solution:
def binaryTreePaths(self, root):
if not root:
return []
paths = []
stack = [(root, '')]
while stack:
node, path = stack.pop()
if not node.left and not node.right:
paths.append(f'{path}{node.val}')
if node.right:
stack.append((node.right, f'{path}{node.val}->'))
if node.left:
stack.append((node.left, f'{path}{node.val}->'))
return paths
Here is also LeetCode's recursive solution:
class Solution:
def binaryTreePaths(self, root):
"""
:type root: TreeNode
:rtype: List[str]
"""
def construct_paths(root, path):
if root:
path += str(root.val)
if not root.left and not root.right: # if reach a leaf
paths.append(path) # update paths
else:
path += '->' # extend the current path
construct_paths(root.left, path)
construct_paths(root.right, path)
paths = []
construct_paths(root, '')
return paths
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.

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".

Is binary tree that is just None can be considered a min-heap tree?

I need to write a recursion for a min-heap binary tree to check if this tree is min-heap. One of the test cases is just NONE.
Is None considered a min-heap tree and returns True, or None is False?
The reason I am asking is that I will reach leaves at some point and their nodes are None and if base case is True then it will return True.
I believe that a none type will be vacuously true as it does not violate the definition of a min-heap tree.
Yes, None is considered a mean-heap tree.
You must mean a Min Heap. When we are dealing with any tree structure the children of a Node are most commonly initialized as None. One of the reasons is that we can easily escape the recursion as such:
def find_node(node, data):
if root is None:
return
if root.data == data:
print "Node found"
find_node(node.left, data)
find_node(node.right, data)
class Node(object):
def __init__(self, data):
self.left = None
self.right = None
self.data = data
In your case you want to check if a tree is min heap by traversing it. You would do something like that
def is_min_heap(root):
#.....check here and then do
return is_min_heap(root.left) and is_min_heap(root.right)
But it depends how you want to handle it. Any one node with no children is a min-heap or a max-heap but it has no meaning. If you want to call
is_min_heap(None) then you are free to do so but it is up to you if you want to say that is True or not.

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

represent binary search trees in python

how do i represent binary search trees in python?
class Node(object):
def __init__(self, payload):
self.payload = payload
self.left = self.right = 0
# this concludes the "how to represent" asked in the question. Once you
# represent a BST tree like this, you can of course add a variety of
# methods to modify it, "walk" over it, and so forth, such as:
def insert(self, othernode):
"Insert Node `othernode` under Node `self`."
if self.payload <= othernode.payload:
if self.left: self.left.insert(othernode)
else: self.left = othernode
else:
if self.right: self.right.insert(othernode)
else: self.right = othernode
def inorderwalk(self):
"Yield this Node and all under it in increasing-payload order."
if self.left:
for x in self.left.inorderwalk(): yield x
yield self
if self.right:
for x in self.right.inorderwalk(): yield x
def sillywalk(self):
"Tiny, silly subset of `inorderwalk` functionality as requested."
if self.left:
self.left.sillywalk()
print(self.payload)
if self.right:
self.right.sillywalk()
etc, etc -- basically like in any other language which uses references rather than pointers (such as Java, C#, etc).
Edit:
Of course, the very existence of sillywalk is silly indeed, because exactly the same functionality is a singe-liner external snippet on top of the walk method:
for x in tree.walk(): print(x.payload)
and with walk you can obtain just about any other functionality on the nodes-in-order stream, while, with sillywalk, you can obtain just about diddly-squat. But, hey, the OP says yield is "intimidating" (I wonder how many of Python 2.6's other 30 keywords deserve such scare words in the OP's judgment?-) so I'm hoping print isn't!
This is all completely beyond the actual question, on representing BSTs: that question is entirely answered in the __init__ -- a payload attribute to hold the node's payload, left and right attribute to hold either None (meaning, this node has no descendants on that side) or a Node (the top of the sub-tree of descendants on the appropriate side). Of course, the BST constraint is that every left descendant of each node (if any) has a payload less or equal than that of the node in question, every right one (again, if any) has a greater payload -- I added insert just to show how trivial it is to maintain that constraint, walk (and now sillywalk) to show how trivial it is to get all nodes in increasing order of payloads. Again, the general idea is just identical to the way you'd represent a BST in any language which uses references rather than pointers, like, for example, C# and Java.

Categories