Traversing a binary tree using linked list - python-3.x

I am trying to do a preorder traversal of binary tree using a linked list.
class BTNode:
"""A node in a binary tree."""
def __init__(self: 'BTNode', item: object,
left: 'BTNode' =None, right: 'BTNode' =None) -> None:
self.item, self.left, self.right = item, left, right
class LLNode:
"""A node in a linked list."""
def __init__(self: 'LLNode', item: object, link: 'LLNode' =None) -> None:
self.item, self.link = item, link
def __str__(self: 'LLNode') -> str:
"""Return an informative string showing self
>>> b = LLNode(1, LLNode(2, LLNode(3)))
>>> str(b)
'1 -> 2 -> 3'
"""
return str(self.item) + (' -> ' + str(self.link) if self.link else '')
def preorder(root: BTNode) -> LLNode:
"""Return the first node in a linked list that contains every value from the
binary tree rooted at root, listed according to an preorder traversal.
>>> b = BTNode(1, BTNode(2), BTNode(3))
>>> repr(preorder(b))
'LLNode(1, LLNode(2, LLNode(3)))'
>>> b2 = BTNode(4, BTNode(5))
>>> b3 = BTNode(7, b, b2)
>>> str(preorder(b3))
'7 -> 1 -> 2 -> 3 -> 4 -> 5'
"""
return _preorder(root)[0]
def _preorder(root: BTNode) -> (LLNode, LLNode):
"""Return the first and last nodes in a linked list that contains every
value from the binary tree rooted at root, listed according to an preorder
traversal.
"""
if not root:
return None, None
left_head, left_tail = _preorder(root.left)
right_head, right_tail = _preorder(root.right)
# change from right_tail = left_tail to right_tail = left_head
if not right_tail:
right_tail = left_head
if not left_head:
left_head = right_head
if left_tail:
left_tail.link = right_head
root_node = LLNode(root.item, left_head)
return root_node, right_tail
I am always getting '7 -> 1 -> 2' instead of '7 -> 1 -> 2 -> 3 -> 4 -> 5' as my output in preorder function. I am not quite sure why. Could someone please tell me how I can edit my current code to fix this problem?

You appear to have an error in your preorder code dealing with returns like LLNode(value, None).
Specifically, when you are traversing BTNode(a, BTNode(b), BTNode(c)) where neither b nor c has children, you don't merge correctly. You'll want to have another look at your logic for this case.

You haven't returned the tail of the list properly. I added some debugging instrumentation to trace the operation of _preorder -- which is something you should do before posting here. Debugging is a critical skill.
indent = ""
def _preorder(root: BTNode) -> (LLNode, LLNode):
"""Return the first and last nodes in a linked list that contains every
value from the binary tree rooted at root, listed according to an preorder
traversal.
"""
global indent
print(indent, " ENTER", root.item if root else "NULL")
if not root:
return None, None
indent += " "
left_head, left_tail = _preorder(root.left)
print (indent, root.item, "Left ", left_head, left_tail, str(left_head))
right_head, right_tail = _preorder(root.right)
print (indent, root.item, "Right", right_head, right_tail, str(right_head))
if not right_tail:
right_tail = left_tail
if not left_head:
left_head = right_head
if left_tail:
left_tail.link = right_head
root_node = LLNode(root.item, left_head)
print (indent, "LEAVE", root.item, right_tail.item if right_tail else "NULL")
indent = indent[2:]
return root_node, right_tail
Output for the full traversal is below. You can see that you never link in the right side properly; I'll leave the repair as an exercise for the student. :-)
ENTER 7
ENTER 1
ENTER 2
ENTER NULL
2 Left None None None
ENTER NULL
2 Right None None None
LEAVE 2 NULL
1 Left 2 None 2
ENTER 3
ENTER NULL
3 Left None None None
ENTER NULL
3 Right None None None
LEAVE 3 NULL
1 Right 3 None 3
LEAVE 1 NULL
7 Left 1 -> 2 None 1 -> 2
ENTER 4
ENTER 5
ENTER NULL
5 Left None None None
ENTER NULL
5 Right None None None
LEAVE 5 NULL
4 Left 5 None 5
ENTER NULL
4 Right None None None
LEAVE 4 NULL
7 Right 4 -> 5 None 4 -> 5
LEAVE 7 NULL
Main: 7 -> 1 -> 2
RESPONSE TO OP UPDATE
Obviously, that fixed part of your problem. Now, let's look at what happens when you return from the calls for node 1's left and right subtrees, and try to link those properly into the linear list:
left_head, left_tail = _preorder(root.left)
# returns 2, None
right_head, right_tail = _preorder(root.right)
# returns 3, None
if not right_tail:
right_tail = left_head
# right_tail is now node 2; this isn't correct: node 3 should be in that spot.
if not left_head:
# left_head is node 2; not an issue now
if left_tail:
# left_tail is None; not an issue now
return root_node, right_tail
# You return nodes 1 and 2;
# you never linked node 2 to node 3.
# You need to fix this.

Related

Reversing second half of a linked list

I have a linked list defined as follows:
1 -> 2 -> 3 -> 4 -> 5 -> null
The goal is to reverse second half as a separate linked list;
Expected output: 5 -> 4 -> null
Here are the two methods I wrote in my linked list class for this purpose;
class SLL:
def reverse(self):
prev = None
curr = self.head
while curr is not None:
temp = curr.get_next()
curr.set_next(prev)
prev = curr
curr = temp
self.head = prev
return self.head
def second_half(self):
fast = self.head
slow = self.head
# fast pointer moves twice as fast as compared to slow pointer
while fast is not None and fast.get_next() is not None:
fast = fast.get_next().get_next()
slow = slow.get_next()
temp = slow.get_next()
slow.set_next(None)
temp = self.reverse(temp)
return temp
But when we invoke the reverse method above, with argument as temp, we're getting following error message;
TypeError: reverse() takes 1 positional argument but 2 were given
Can I please get some help how to rectify this error and obtain desired output? thanks
The goal is to reverse second half as a separate linked list;
That would mean the result should be an instance of SLL. That means you should somewhere create that new linked list with a call of SLL.
So I'd suggest to change that function as follows:
def second_half(self):
fast = self.head
slow = self.head
while fast is not None and fast.get_next() is not None:
fast = fast.get_next().get_next()
slow = slow.get_next()
temp = slow.get_next()
slow.set_next(None)
# changed code below:
newlist = SSL()
newlist.head = temp;
newlist.reverse()
return newlist

Comparing whether two trees are same or not? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 12 months ago.
Improve this question
This is the question
https://leetcode.com/problems/same-tree/
Question:
Given the roots of two binary trees p and q, write a function to check if they are the same or not.
Two binary trees are considered the same if they are structurally identical, and the nodes have the same value.
I have used Python for solving this problem
This is my approach. Could you tell why it failed the given test case?
https://leetcode.com/submissions/detail/661792014/
My approach:
#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 isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
a = m = p
b = n = q
if p is None and q is None:
return True
elif p is None or q is None:
return False
while a:
if a.val != b.val:
return False
if a.left:
a = a.left
if b.left:
b = b.left
else:
return False
else:
if b.left:
return False
else:
break
while m:
if m.val != n.val:
return False
if m.right:
m = m.right
if n.right:
n = n.right
else:
return False
else:
if n.right:
return False
else:
break
return True
The problem is that in your while loop, m will go all the way down via the same-side child (first loop: left, second loop right), but when it reaches the end, it will not backtrack and then look at all the opposite sided subtrees it had skipped along the way.
For example, take this tree:
4
/ \
2 6
/ \ / \
1 3 5 7
The first loop will check the left side (i.e. 4, 2 and 1), and the second loop will check the right side (i.e. 4, 6, 7), but there is no provision to go down the tree in a zig-zag way, so the nodes with 3 and 5 are never checked.
What you need here, is recursion. Solve the problem recursively for the left subtree and again recursively for the right subtree. If either returns that there is a difference, stop, and return False. If both calls give a positive result, and also the current node is equal, return True.
The solution (spoiler):
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
return bool(not p and not q or
p and q and p.val == q.val
and self.isSameTree(p.left, q.left)
and self.isSameTree(p.right, q.right))

Interviewbit - Merge k sorted linked lists: heappop returns max element instead of min

I'm solving the Interviewbit code challenge Merge K Sorted Lists:
Merge k sorted linked lists and return it as one sorted list.
Example :
1 -> 10 -> 20
4 -> 11 -> 13
3 -> 8 -> 9
will result in
1 -> 3 -> 4 -> 8 -> 9 -> 10 -> 11 -> 13 -> 20
The Python template code is:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# #param A : list of linked list
# #return the head node in the linked list
def mergeKLists(self, A):
pass
Here's my python 3 solution for the same:
from heapq import heapify, heappop, heappush
class Solution:
# #param A : list of linked list
# #return the head node in the linked list
def mergeKLists(self, A):
minheap = [x for x in A]
# print(minheap)
# heapify(minheap)
# print(minheap)
head = tail = None
# print(minheap)
while minheap:
# print(minheap)
heapify(minheap)
print([x.val for x in minheap])
minimum = heappop(minheap)
print(minimum.val)
if head is None:
head = minimum
tail = minimum
else:
tail.next = minimum
tail = minimum
if minimum.next:
heappush(minheap, minimum.next)
return head
With the print commands that are uncommented, you'll notice that in the intermediate runs of the while loop, heappop returns the largest element, as if we were dealing with a max heap, which we're not!
That's the place where the answer is going wrong as far as I can see. Can anyone suggest the reason for why heappop is working like this? And how that can be corrected?
When I run your code locally with sample data, I get an error on:
heapify(minheap)
TypeError: < not supported between instances of ListNode and ListNode
This is expected. The template definition of ListNode shows no support for making comparisons, and a heapify function will need to compare the items in the given list.
As the class ListNode is already defined by the code-challenge framework, it is probably better not to try to make that class comparable.
I would propose to put tuples on the heap which have list node instances as members, but have their val attribute value come first, followed by the number of the list (in A) they originate from. As third tuple member you'd finally have the node itself. This way comparisons will work, since tuples are comparable when their members are. And since the second tuple member will be a tie-breaker when the first member value is the same, the third tuple member (the list node instance) will never be subject to a comparison.
Unrelated to your question, but you should only heapify once, not in each iteration of the loop. The actions on the heap (heappush, heappop) maintain the heap property, so there is no need for calling heapify a second time. If you do it in each iteration, you actually destroy the efficiency benefit you would get from using a heap.
Here is your code updated with that change:
from heapq import heapify, heappop, heappush
class Solution:
def mergeKLists(self, A):
# place comparable tuples in the heap
minheap = [(node.val, i, node) for i, node in enumerate(A)]
heapify(minheap) # call only once
head = tail = None
while minheap:
# extract the tuple information we need
_, i, minimum = heappop(minheap)
if head is None:
head = minimum
tail = minimum
else:
tail.next = minimum
tail = minimum
minimum = minimum.next
if minimum:
# push a tuple, using same list index
heappush(minheap, (minimum.val, i, minimum))
return head

Is there a way to simplify the filtering of inputs

I am trying to find a neater way to do the following input check:
def intInputCheck():
while True:
try:
INPUT = int(input("INPUT -> "))
return INPUT
except ValueError:
print("Please only input integers")
def createGroup():
possibleSupervisors = ["USER1","USER2"] #etc
print("Possible supervisors:\n{}".format(possibleSupervisors))
for i in range(0, len(possibleSupervisors)):
print(i, ":", possibleSupervisors[i][0])
"""
supervisor = intInputCheck
while supervisor() not in range(0, len(possibleSupervisors)):
print("Please only input numbers in the range provided")
"""
#The above kinda works, but i cant then use the variable "supervisor"
"""
supervisor = intInputCheck()
while supervisor not in range(0, len(possibleSupervisors)):
supervisor = intInputCheck()
print("Please only enter integers in the given range")
"""
"""
The above works, however I end up giving out two print statements if
the user doesnt input an integer which I don't want, I want it to
only output the print statement if that specific error occurs, in
this, If a str is entered, the func will output "only enter ints" and
then the while will output "only ints in given range" which is a pain
"""
I am also trying to see if closures could be useful in simplifying this code, the reason I want to do this is to make my code neater (I think having the same input before and then inside a while loops looks bad).
The reason for the function is so that I can use this input checking function in various parts of my program
You can "enhance" your validator function - you probably should resort to two different functions because this one does too much for one single function, but here we go:
def intInputCheck(text,error,options=[]):
"""Prints 'text' and optional a list of (1-based) 'options' and loops
till a valid integer is given. If 'options' are given, the integer must
be inside 1..len(options).
The return is either an integer or a tuple of the 1-based list index and the
corresponding value from the list."""
msg = [text]
test = None
if options:
test = range(1,len(options)+1)
for num,t in enumerate(options,1):
msg.append("{:>2} : {}".format(num,t))
msg.append("Choice -> ")
while True:
try:
INPUT = int(input('\n'.join(msg)))
if test is None:
return INPUT
elif INPUT in test:
return (INPUT,options[INPUT-1])
else:
raise ValueError
except ValueError:
print(error)
k = intInputCheck("INPUT -> ","Please only input integers")
sup = intInputCheck("Possible supervisiors:",
"Choose one from the list, use the number!",
["A","B","X"])
print(k)
print(sup)
Output:
# intInputCheck("INPUT -> ","Please only input integers")
INPUT -> a
Please only input integers
INPUT -> b
Please only input integers
INPUT -> 99
# intInputCheck("Possible supervisiors:", "Choose one from the list, use the number!",
# ["A","B","X"])
Possible supervisiors:
1 : A
2 : B
3 : X
Choice -> 8
Choose one from the list, use the number!
Possible supervisiors:
1 : A
2 : B
3 : X
Choice -> 6
Choose one from the list, use the number!
Possible supervisiors:
1 : A
2 : B
3 : X
Choice -> 2
The results:
# k
99
# sup
(2, 'B')

AttributeError: 'NoneType' object has no attribute 'left'

Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
class Solution(object):
def hasPathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
l = []
def helper(tree, total):
if not tree.left and not tree.right:
l.append(total)
else:
helper(tree.left, total + tree.val)
helper(tree.right, total + tree.val)
return l
if root:
helper(root, 0)
return sum in l
return False
Can anyone tell me what's wrong with my code???
The primary issue is that your helper improperly handles cases when a node's left or right child are None. It will recurse on both children and crash attempting to access None.left or None.right.
Beyond that, there is no need for a list here; you can return True if you reach a leaf and find that your running total equals the sum. This is more efficient because you avoid traversing the rest of the tree after finding a solution and avoid traversing the list to find your target after the tree search is over.
Also worth mentioning, Leetcode gives the parameter sum, which overwrites a builtin function. I usually rename such variables and took the liberty to do so here.
def hasPathSum(self, root, target):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
def helper(tree, total=0):
if not tree.left and not tree.right:
return target == total + tree.val
if tree.left and helper(tree.left, total + tree.val):
return True
if tree.right and helper(tree.right, total + tree.val):
return True
return False
return helper(root) if root else False

Resources