Convert a BST to sorted list - python

The problem seems straight forward.
You must use the same tree and make the right child pointer as the next pointer in the list.
so the algorithm i used is as follows:
def inorder(node, prev, head):
if(node == NULL):
return;
inorder(node.left, prev, head)
node.right = prev
if(!prev):
head = node
prev = node
inorder(node.right, prev, head)
can anyone point out where exactly i am going wrong because it just doesn't seem to work.

The first bug that I saw is that you're assigning to head and prev inside of inorder and are hoping that somehow this will affect head and prev inside of previous calls to inorder. But it does not.
What you need to do instead is to have inorder return information that you want, then assign them within the parent call.

Related

How tail works within a linked structures?

I am new into programming and I am learning more complex Data Structures using Python and I find difficult to understand the concept of adding an element to a linked list using a head and a tail.
class Bag:
def __init__(self):
self._head = None
self._tail = None
self._size = 0
def add(self, item):
newNode = _BagListNode(item)
if self._head is None:
self._head = newNode
else:
self._tail.next = newNode
self._tail = newNode
self._size += 1
class _BagListNode(object):
def __init__(self, item):
self.item = item
self.next = None
def __repr__(self):
return f"{self.item}"
The point is when I add the first element, everything clear. As the head is None at first, it'll add the newNode to both tail and head. The problem starts when I add the second element: I do not understand why the second element is added to the element that has been added before, at the same time with the self._tail when this line of code self._tail.next = newNode is executed. After this line of code, the self._tail becomes the second element and this seems pretty logical as I have to keep tracking the tail as I keep on adding elements and self._head now have two elements but in code there is no line of code where to self._head is added a new element.
For example:
bag = Bag()
bag.add(1)
bag.add(2)
bag.add(3)
print(bag._head.item, bag._head.next.item, bag._head.next.next.item)
and the result is:
1 2 3
I hope my question is clear enough. I appreciate your time so much. Thank you!
After this line of code, the self._tail becomes the second element and this seems pretty logical as I have to keep tracking the tail as I keep on adding elements and self._head now have two elements but in code there is no line of code where to self._head is added a new element.
I think what you might be missing here is that self._head is not a Bag in and of itself, but rather a pointer to a _BagListNode object.
When new items are added on to the bag, they are affixed as the next node on the previous tail, becoming the new tail. This does not affect the head at all in your implementation. An alternative, perhaps clearer, implementation could simply make an item the new head of the list, as pictured here:
One tip my data structures professor gave me was to draw a picture of what is happening to improve your intuitive understanding. You might consider drawing a picture of what happens when the third item is inserted into the bag.

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

InOrder Traversal in Python

The problem I am tackle with is to find the first occurrence node in its inorder traversal in a BST.
The code I have is given below
def Inorder_search_recursive(node,key):
if not node:
return None
InOrder_search_recursive(node.lChild)
if node.value==key:
return node
InOrder_search_recursive(node.rChild)
This code always return None, what's wrong with it. I think I've return node when I find a node with value k. Why cannot python pass this node???Thanks in advance
When you call yourself recursively, like this:
InOrder_search_recursive(node.lChild)
That's just a normal function call, like any other. It just calls the function and gets back a result. It doesn't automatically return the value from that function, or do anything else.
So, you do the left-subtree search, ignore the results, then go on to check node.value == key, and, if that fails, you do the right-subtree search, again ignore the results, and fall off the end of the function, meaning you return None.
To make this work, you need to return the value you got back. But, of course, only if it's not None.
Also, you forgot to pass the key argument down to the recursive call, so you're just going to get a TypeError. (I'm guessing your real code doesn't have this problem, but since you didn't show us your real code, or a working example, I can't be sureā€¦)
So:
def Inorder_search_recursive(node, key):
if not node:
return None
result = InOrder_search_recursive(node.lChild, key)
if result is not None:
return result
if node.value==key:
return node
return InOrder_search_recursive(node.rChild, key)
(You don't need the not None check for the right-side search, because if it returns None, we have nothing else to try and are just going to return None anyway.)
My other answer gives the novice-friendly solution, but if you want more powerful and concise answer:
def Inorder_search_recursive_all(node, key):
if not node:
return
yield from InOrder_search_recursive(node.lChild, key)
if node.value==key:
yield node
yield from InOrder_search_recursive(node.rChild, key)
This generates all matches in the tree, in order. And it gives them to you as an iterator, so if you just want the first, you can stop as soon as you find one, with no wasted work:
def Inorder_search_recursive(node, key):
return next(Inorder_search_recursive_all(node, key), None)
The tutorial section on Iterators and the following section on Generators explains most of how this works. The only missing bit is an explanation of yield from, which is explained in PEP 380.
Since your problem is to find the first occurrence node in its inorder traversal, you should 1) traverse the tree in-order and 2) stop when you find the first occurrence.
def search(node, key):
if node is None:
return None
# Search the left subtree and return early if key is found
n = search(node.lChild, key)
if n is not None:
return n
# Check middle and return early if key is found
if node.value == key:
return node
# Search right subtree
return search(node.rChild, key)

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