I was trying to find out a way to perform in-order tree transversal iteratively without using a tuple. Not sure why but the list is going from [] to [None] and runs the while loop one more time
https://leetcode.com/problems/binary-tree-inorder-traversal/description/
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
stack = [root]
res = []
while stack:
print("start",stack)
curr = stack.pop()
if curr.left:
temp = curr.left
curr.left = None
if curr.right:
stack.append(curr.right)
curr.right = None
stack.append(curr)
stack.append(temp)
else:
res.append(curr.val)
if curr.right:
stack.append(curr.right)
print("end",stack)
return res
when running this code with testcase [1], the output when printing is
start [TreeNode{val: 1, left: None, right: None}]
end []
start [None]
this means at the end of the while loop stack = [], so the while loop should end. However, all of a sudden the stack = [None] and runs one more loop. Very confused why this happens. I put a print in front of every append but found nothing. It is solved by adding a check for if curr is None but I still have no idea why this happens. If anyone knows please help!
UPDATE:
Copy and pasted into hackerrank's same question and it worked properly. Could this be a bug in leetcode?????
https://www.hackerrank.com/challenges/tree-inorder-traversal/problem
I am assuming what you are trying to do here is convert a tree to an in-order list of elements to look at. It is possible you're trying to do something else, but if so, it is unclear.
def inOrderTraversal(ls: TreeNode) -> List[int]:
// Recurse into the left branch
if isinstance(ls.left, int):
left = [ls.left]
else: // Assumes left is a TreeNode
left = inOrderTraversal(ls.left)
// Recurse into the right branch
if isintance(ls.right, int):
right = [ls.right]
else:
right = inOrderTraversal(ls.right)
return left + right
This almost certainly won't work right out of the box, but it is a demonstration of what you're going for. You need to determine if the left branch should be recursed down, then the right. If the left or right branches are simply ints, then you need to add them as elements.
Your while loop is a misdirection - you are already setting stack to be a list of length one (the root element). The while loop isn't doing anything since you have only one element. You do not need to set anything to None for this sort of task - just forget about whatever you were iterating over once you have the data you need.
Related
I encountered this issue while working on a basic graph traversal leetcode problem.
I've included my entire answer below, a basic BFS. But my question really concerns to what is happening with my set() variable named "seen" in the while loop.
Basically, I initialize it to an empty set before the while loop, expecting it to cumulatively grow with each value popped from the queue being added to this set, from within the while loop.
Instead, it seems that on each iteration of the while loop, the contents of "seen" somehow revert back to being empty, despite not resetting it in the code.
I have added some comments to clarify where I'm referring to:
import queue
def canFinish(numCourses, prerequisites):
prereqs = {}
buildPrereqs(prereqs, prerequisites)
for course in range(numCourses):
seen = set() # initialize set outside of while loop
q = queue.Queue()
q.put(course)
while (not q.empty()): # while loop
popped = q.get()
print("popped:", popped)
print("seen at beginning:", seen)
if popped in seen:
return False
seen.add(popped) # populate set, but resets before next iteration (?)
if popped in prereqs:
for prereq in prereqs[popped]:
q.put(prereq)
print("seen at end:", seen)
return True
def buildPrereqs(prereqs, prerequisites):
prereqs = {}
for pair in prerequisites:
course = pair[0]
prereq = pair[1]
if not course in prereqs:
prereqs[course] = set()
prereqs[course].add(prereq)
print(canFinish(2, [[1,0],[0,1]]))
If I run canFinish(2,[[1,0],[0,1]]), which is one of the example cases at the leetcode link, I get the following output from my print statements:
popped: 0
seen at beginning: set()
seen at end: {0}
popped: 1
seen at beginning: set()
seen at end: {1}
Whereas I am expecting something like this:
popped: 0
seen at beginning: set()
seen at end: {0}
popped: 1
seen at beginning: {0}
seen at end: {0, 1}
I tried using a dictionary in place of the set() and encountered the same thing. Can someone help me identify the source of this Python behavior?
I have started to learn Recursion , I seem to understand the concept , but at the same time I feel completely lost.
For eg, I was trying to solve find the ancestors of a given node in Binary Tree
My Tree:
1
/ \
7 9
/ \ / \
6 5 2 3
My code :
def findAns(node,target,ans):
if node is None:
return ans
ans.append(node.data)
if node == target:
return ans[:-1] #return the list except the last item(whch will be the target node)
ans = findAns(node.left,target,ans)
ans = findAns(node.right,target,ans)
del ans[-1] #Delete last node while backtracking
return ans
ans=[]
findAns(root,target,ans) #target node is 5
print(ans)
print(ans[:-1])
OUTPUT :
[1,7,5]
[1, 7]
I am unable to understand the below questions,
When backtracking is completed for the entire tree, list 'ans' will be empty # root position, how come I am able to get the value [1,7,5] in my list ?
when the if condition is satisfied , I return ans[:-1] , because I should not include the target node, but how come when I print 'ans' I am getting the target node as well ?
In the same 'if condition' , if I return ans instead of ans[:-1], y code doesn't work, I get back an empty 'ans' list, why is that ?
But to avoid the confusion I changed my code as mentioned below, I used a global variable . However I don't think it is efficient , any resource or material or explanations for my above question would be of great help to me. Thanks !
Code with Global variable:
_ans=[]
def findAns(node,target,ans):
global _ans
if node is None:
return ans
ans.append(node.data)
if node == target:
_ans.append(list(ans[:-1]))
#return ans[:-1]
ans = findAns(node.left,target,ans)
ans = findAns(node.right,target,ans)
del ans[-1]
return ans
ans=[]
findAns(root,target,ans)
_ans[0]
You expected the root after backtracking returns empty list, well it does. The problem is the function returns the empty list but you don't catch it:
res=findAns(root, target, ans)
print(res)
Output:
[]
From question 1 you may still think printing ans will be the same as catching the return value, but when you return ans[:-1] you have already lost the original list you passed in. This is heavily related to list in python being mutable and list[:-1] will return a completely new list and modifying the new list won't affect the ans you first passed in. So after it returns a new list, the backtracking ans = findAns(root.right, target, ans) takes a new list, after that changing the 'new' ans won't change the 'old' ans(the one used to print)
If you change to return ans then it will print out empty list. The problem is every time you iterate through a step in the recursion you append a new node but at the same time you delete one last node, which results in add one and delete one being zero.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
regularListHead = ListNode(-1)
regularListHead.next = head
reverseListHead = head
reverseListPrev = None
reverseListCurrent = reverseListHead
while reverseListCurrent != None:
reverseListNext = reverseListCurrent.next
reverseListCurrent.next = reverseListPrev
reverseListPrev = reverseListCurrent
reverseListCurrent = reverseListNext
reverseListHead = reverseListPrev
a = regularListHead
In my code, I am trying to convert the original list to reversed list and compare both for checking palindrome but when I do the operations to reverse the reverselist then original list is also updated. I am new to python can anyone let me know why this is happening and what could be done to achieve what I want
You only create one new node. All the other nodes are the nodes of your original list, and you update their next properties so the list is reversed.
So if you want to have both the original and the reversed list, you'll have to have two lists, each having their own nodes:
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None:
return
# Create reversed list
revHead = ListNode(head.val)
current = head.next
while current is not None:
revHead = ListNode(current.val, revHead)
current = current.next
# Compare original with the reversed one
while head is not None:
if head.val != revHead.val:
return False
head = head.next
revHead = revHead.next
return True
This is however not optimal: the comparison loop should only need to perform 𝑛/2 comparisons, not 𝑛.
You may even ask yourself why you would create a linked list for the reversed version: as it is only needed for the palindrome check, you might as well create a standard list, and then do the palindrome check with just that list, which is a piece of cake.
But more importantly, creating a new list (whether a linked list or normal "array" list) requires O(n) extra space. If you are interested in an algorithm for doing this without creating new nodes, then try to implement this:
Count the number of nodes in the list
Use that to identify the first node of the second half of the list. If the number of nodes is odd, let this be the node after the center node.
Apply an in-place list reversal algorithm on that second half. Now you have two shorter lists.
Compare the values in those two lists to see whether they are equal (ignore the center node if there was one). Remember the outcome (false or true)
Optionally repeat step 3 so the reversal is rolled back, and the list is back in its original state. (This is "nice" towards the caller of your function)
Return the result that was found in step 4.
I wrote code for a DFS after reading about what it is but not actually seeing the code. I did this to challenge myself (I always have believed that to learn something new you must always first challenge yourself). The thing is after I wrote my code, I compared my implementation to the one in the book I read it in (Introduction to the Design and Analysis of Algorithms - A. Levitin) and it is completely different. So now I am wondering well it works as intended... is it still a DFS?
I made the implementation to solve a maze. I will give a rundown on my code and also upload the code here (Some people hate reading other people's code while others do.)
Algorithm (What I understood and did):
Convert maze into a graph/map
Set start position as current node and run loop in which...
I choose one of the adjacent nodes as the next current node and do this until I stumble upon a dead end. Also I am adding each node I pass through into a list that acts as my stack.
Once I am at a dead end, I keep poping items from the stack and each time I pop, I check if it has adjacent nodes that have not been visited.
Once I have found an unvisited adjacent node, we continue the entire process from step 3.
We do this until current node is the end position.
Then I just retrace my way back through the stack.
Here is my code:
# Depth First Search implementation for maze...
# from random import choice
from copy import deepcopy
import maze_builderV2 as mb
order = 10
space = ['X']+['_' for x in range(order)]+['X']
maze = [deepcopy(space) for x in range(order)]
maze.append(['X' for x in range(order+2)])
maze.insert(0, ['X' for x in range(order+2)])
finalpos = (order, order)
pos = (1, 1)
maze[pos[0]][pos[1]] = 'S' # Initializing a start position
maze[finalpos[0]][finalpos[1]] = 'O' # Initializing a end position
mb.mazebuilder(maze=maze)
def spit():
for x in maze:
print(x)
spit()
print()
mazemap = {}
def scan(): # Converts raw map/maze into a suitable datastructure.
for x in range(1, order+1):
for y in range(1, order+1):
mazemap[(x, y)] = []
t = [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]
for z in t:
if maze[z[0]][z[1]] == 'X':
pass
else:
mazemap[(x, y)].append(z)
scan()
path = [pos] # stack
impossible = False
while path[-1] != finalpos:
curpos = path[-1]
i = 0
while i < len(mazemap[curpos]):
if mazemap[curpos][i] in path:
del mazemap[curpos][i]
else:
i += 1
nextpos = None
if mazemap[curpos] == []:
while nextpos == None:
try:
wrongpos = path.pop(-1)
if mazemap[wrongpos] == []:
pass
else:
path.append(wrongpos)
# nextpos = choice(mazemap[wrongpos])
nextpos = mazemap[wrongpos][-1]
mazemap[wrongpos].remove(nextpos)
except IndexError:
impossible = True
break
else:
# nextpos = choice(mazemap[curpos])
nextpos = mazemap[curpos][-1]
if impossible:
break
path.append(nextpos)
if not impossible:
for x in path:
if x == pos or x == finalpos:
pass
else:
maze[x[0]][x[1]] = 'W'
else:
print("This maze not solvable, Blyat!")
print()
spit()
As always, I greatly appreciate your suggestions!
Your algorithm looks DFS to me. DFS means exploring the path as deep as possible, backtrack to the previous node only if there is no solution and your algorithm works in a similar way by popping nodes from the stack. You just mimic the recursion stack using your own stack so it looks quite different from the standard solution.
Essentially, all recursive algorithms can be simulated using stack and loop. But most of the time doing this will make the algorithm much less readable. To tackle a difficult problem, I think the usual way to do it is to first come up with the recursive solution. After making sure the recursive solution is bug-free, then start implementing the iterative version using stack if you care a lot about the efficiency.
Other Suggestion:
if mazemap[curpos][i] in path: is a O(n) operation since path is a normal list. Consider using a separate hash set to store visited nodes and use the set to check repetition instead to make it O(1).
This question already has answers here:
Best algorithm to test if a linked list has a cycle
(12 answers)
Closed 9 years ago.
Could someone please let me know the best way to prove a linked list contains a loop?
I am using an algorithm with two pointer, one is moving slow with one steps and one is moving faster with two steps.
class Node(object):
def __init__(self, value, next=None):
self.next=next
self.value=value
def create_list():
last = Node(8)
head = Node(7, last)
head = Node(6, head)
head = Node(5, head)
head = Node(4, head)
head = Node(3, head)
head = Node(2, head)
head = Node(1, head)
last.next = head
return head
def is_circular(head):
slow = head
fast = head
while True:
slow = slow.next
fast = fast.next.next
print slow.value, fast.value
if slow.value == fast.value:
return True
elif slow is fast:
return False
if __name__ == "__main__":
node = create_list()
print is_circular(node)
A good algorithm is as follows, it may very well be the best. You do not need to copy the list or anything, like that, it can be done in constant space.
Take two pointers and set them to the beginning of the list.
Let one increment one node at a time and the other two nodes at a time.
If there is a loop at any point in the list, they will have to be pointing to the same node at some point (not including the starting point). Obviously if you reach the end of the list, there is no loop.
EDIT:
Your code, but slightly edited:
def is_circular(head):
slow = head
fast = head
while fast != None:
slow = slow.next
if fast.next != None:
fast = fast.next.next
else:
return False
if slow is fast:
return True
return False
Don't know about best, but simpliest I can think of is
>>> import json
>>> l = []
>>> l.append(l)
>>> json.dumps(l)
Traceback (most recent call last):
...
ValueError: Circular reference detected
I would test it just like in any other language:
Start traversing the list from the start, adding all visited elements into a data structure (e.g. a set) with fast insertion and lookup. If you hit the end of the list without seeing any element twice, the list is not circular. If you see an element twice, the list is circular.
If neither is true, the list is infinite. :-)
Here is way to do it:
Start from a node & store its pointer in variable
Start visiting next elements till end or the start node is revisited.
if end is reached then it is not circular else circular