I am learning data structure in Python and I've encountered code that I don't understand.
I have a node class
class Node():
def __init__(self, data=None, next=None):
self.data = data
self.next = next
and linkedlist class
class LinkedList():
def __init__(self):
self.head = Node()
...
...
def reversePrint(self, node=None):
if node == None:
node = self.head
if node.next:
self.reversePrint(node.next)
print(node.data)
the methods append and delete and find are woking fine
But I can't seem to understand how this method reverse_print renders the linkedlist nodes in reverse order i would expect to print only the last node data rather than the whole linkedlist nodes in reverse order.
Here's my code
Suppose your linked list is
Node A: data="alpha"
Node B: data="beta"
Node C: data="gamma"
You call ll.reversePrint() and node gets set to the head, which is Node A.
reversePrint with node=Node A, checks for the next node (which is Node B), and calls reversePrint, passing in Node B.
-> reversePrint with node=Node B, checks for the next node (which is Node C), and calls reversePrint, passing in Node C.
-> -> reversePrint with node=Node C, checks for the next node (there isn't one).
-> -> reversePrint with node=Node C prints the node's data: "gamma".
-> reversePrint with node=Node B prints the node's data: "beta".
reversePrint with node=Node A prints the node's data: "alpha".
The arrows indicate the depth of recursion.
The magic happens in these two lines:
if node.next:
self.reversePrint(node.next)
If the node has a successor, it'll call the same method again, all the way up to the end of the list. Then it'll unwind the call stack and, in reverse order, print the data in each node (because print(node.data) appears after the recursive call to reversePrint). If you switch the print statement and the recursive call it'll print it in forward order, which is a bit easier to grasp.
Related
I'm new to programming, so excuse the possibly stupid question.
I'm doing leetcodes and I got to the linked lists. I think I understand them okay, it's just that I don't know how to test my code/call my function(?)
Problem I'm working on
Here's my code, I know it works since I uploaded it onto leetcode, but I would still like to be able to run it on my machine.
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
I guess I have two different problems:
the "Optional[ListNode]) -> Optional[ListNode]:" part
and the actual calling of the function
Some questions before used the "typing" module functions like "List", so I would simply import them and they wouldn't be a problem. But I'm not really sure what to do here
To check my solutions, I write a short piece of code that I can put example inputs into
Solution = Solution()
print(Solution.middleNode(head = [1,2,3,4,5,6]))
But isn't the "head" there, just a normal list? Do I have to create an extra function separately to create the "links". I've seen the creation of a linked list done by calling a function every time you want to add a new node. So would I use a for loop to add my example case?
well if you looking for internal boilerplate, below is code for that.
here you need to create classes for nodes, linked list and solutions,.
then with the given number, you need to create a linkedlist object.
this above part is done in leetcode by themself and this object is passed to class Solution method middleNode, where OP code run and give result. next once output is got it is compared with existing solution
# Node class for individual nodes
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
# linked list class, to create a linked list of the list nodes
class LinkedList:
def __init__(self):
self.head = None
# adding a node element to linked list
def add(self, node):
if self.head is None:
self.head = node
else:
curr = self.head
while curr.next:
curr = curr.next
curr.next = node
curr = node
# printing element of existing linked list
def print_ll(self):
curr= self.head
while curr:
print(curr.val)
curr= curr.next
# leetcode solution class
class Solution:
def middleNode(self, head) :
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
# value with which we create list nodes
values = [1,2,3,4,5,6,7,8,9,10]
ll = LinkedList() # create a linked list class object
for i in values:
node = ListNode(i) # creating a list node
ll.add(node) # adding list node to linked list
#ll.print_ll() # printing linked list
x = Solution().middleNode(ll.head) # passing linked list object to leetcode solution method and getting result
while x: # printing result
print(x.val)
x=x.next
I think the problem on LeetCode is poorly worded. head = [1,2,3,4,5] is not really a head as it should only refer to the first item in the list - there seems to be a bit of hidden boilerplate code that creates a linked list from input list and an output list from output node.
Here's an example code that works similiar to the LeetCode task.
from typing import Optional
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
inp = [1,2,3,4,5,6]
next = None
for i in reversed(inp):
next = ListNode(i, next) # next points to head at the end of the loop
res = Solution().middleNode(next)
out = []
while res:
out.append(res)
res = res.next
print([o.val for o in out])
to make your code work on your machine you have to implement a couple of things:
First, for your first answer, you have to implement the class ListNode given at the top of the leetcode-page:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
The you import "Optional" from typing:
from typing import Optional
Then you have the prerequisites for your code.
You have to initialise the class, as you have mentioned. The only problem here is, that your variable has the same name as your class, what could cause trouble later.
To finish, you have to call your function as you already did, with one little difference: This function has to be called with "head" as a variable of type ListNode, not List, and gives you back a variable of the type ListNode.
In a nutshell, this would be my solution (of course you can and as much ListNodes as you want):
from typing import Optional
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
# Initialise class
s = Solution()
# Defining nodes of the list, assigning always the next node.
seven = ListNode(7)
six = ListNode(6, next=seven)
five = ListNode(5, next=six)
four = ListNode(4, next=five)
three = ListNode(3, next=four)
two = ListNode(2, next=three)
one = ListNode(1, next=two)
# Calling your function (with "one" as your head node of the list)
# NOTE: As this gives you back an attribute of type ListNode, you have to access the "val" attribute of it to print out the value.
print(s.middleNode(one).val)
How to initialize head in this implementation of LinkedList in python?
I know a Node class can be defined that will make things easier.But I am trying to do in this way(like in C++).
class LinkedList:
def __init__(self,val=None,next=None):
self.val = val
self.next = next
If you just have this one class, then it actually serves as a Node class, and you lack the container class that would have the head member.
This has as consequence that the program will have to manage the head itself. The variable that will be used to reference a linked list could be that head variable, but that also means an empty list will be represented by None.
For instance:
head = None # Empty list
head = LinkedList(1, head) # Add the first node to it
head = LinkedList(2, head) # Prepend the second node to it
head = LinkedList(3, head) # Prepend the third node to it.
# Print the values in the list:
node = head
while node:
print(node.val, end=" ")
node = node.next
print()
Output will be:
3 2 1
I want to implement a Binary Tree in Python. I sumit my code. What I would like to do is to
set the height of the Binary Tree with the variable L. But, when I implement the code, it seems that the code has created a Binary Tree that is greater than I expected.
I arrive to this conclusion because when I set the height as 1 and I do print(node.right.right.right), I still get 1.
class Tree:
def __init__(self,x,left=None,right=None):
self.x=x
self.left=left
self.right=right
def one_tree(self,node):
node=Tree(1)
node.right=Tree(1)
node.left=Tree(1)
return node
node=Tree(1)
node=node.one_tree(node)
L=1
while L>0:
node=node.one_tree(node)
node.left=node
node.right=node
L=L-1
print(node.right.right.right.right)
I found a problem with your code. one_tree method overwrites the argument node itself.
class Tree:
def __init__(self, x, left=None, right=None):
self.x = x
self.left = left
self.right = right
def one_tree(self, node):
node = Tree(1) # This assignment statement overwrites the argument 'node'.
node.right = Tree(1)
node.left = Tree(1)
return node
one_tree method gets an argument node but the first line of this method overwrites it like this node = Tree(1). Whatever the method gets as an argument, the method always has a new instance of Tree as node variable.
Several issues:
one_tree doesn't use the node that you pass as argument, so whatever you pass to it, the returned tree will always have 3 nodes (a root with 2 children).
one_tree is a method that doesn't use self, so it really should be a class method, not an instance method
If the intended algorithm was to add a level above the already created tree, and have the new root's children reference the already existing tree, then you would need to only create one node, not three, and let the two children be the given node.
Not a problem, but your loop is not really the "python way" to loop L times. Use range instead.
This means your code could be this:
class Tree:
def __init__(self, x, left=None, right=None):
self.x = x
self.left = left
self.right = right
node = Tree(1)
L = 1
for _ in range(L):
node = Tree(1, node, node)
Now you should still be careful with this tree, as it only has L+1 unique node instances, where all "nodes" on the same level are actually the same node instance. All the rest are references that give the "impression" of having a tree with many more nodes. Whenever you start mutating nodes in that tree, you'll see undesired effects.
If you really need separate node instances for all nodes, then the algorithm will need to be adapted. You could then use recursion:
def create(height):
if height == 0: # base case
return Tree(1)
return Tree(1, create(height-1), create(height-1))
L = 1
node = create(L)
0
/ \
1 2
/ \ /|\
3 4 5 6 7
I'm trying to return the nodes without children (3,4,5,6,7) from an object using a recursive function.
It works using a global variable and this function:
def find(self, nodes):
if not hasattr(self, 'children'):
nodes.append(self)
else:
for i in self.children:
i.find(nodes)
nodes = []
node.find(nodes) # can be any node (0,1,2,3,4, etc.)
print(nodes)
But I would like to use return in my function. I tried something like this:
def find2(self):
if not hasattr(self, 'children'):
return self
else:
for i in self.children:
return i.find2()
nodes = root.find2()
print(nodes)
But I get only 1 node. I also tried passing an array like on this post: PYTHON Return a list from a recursive function. But I don't get the result I want because tree structure (I guess)...
I'm stuck, can you help me? How to "return the result for each iteration of the recursive function to a variable"? Thank you
This is the input example:
0
/ \
1 2
/ \ /|\
3 4 5 6 7
Think about what you want the (recursive) function to return for each of the nodes:
when called on the root (0), it should return the full result (3, 4, 5, 6, 7)
when called on a leaf node (e.g. 5), it should return that node (5)
for any other node (e.g. 1), it does as if that was a root node of a smaller tree
So, it sometimes returns one result and sometimes many.
The error in your code is here, because the function ends on the first return, it does not return many:
for i in self.children:
return i.find2()
There are two solutions:
make a generator function, or
make a function which returns a list (make it return a list always, even if it has just one element!)
So, option 1:
def find(self):
if not hasattr(self, 'children'):
yield self
else:
for child in self.children:
yield from child.find()
option 2:
def find(self):
if not hasattr(self, 'children'):
return [self]
else:
rtn = []
for child in self.children:
for result in child.find():
rtn.append(result)
return rtn
I prefer the generator. Additionally, I don't particularly like the fact that some of your nodes have a children attribute, while others do not. I would make sure all of them had children, which could be empty or non-empty. Then, the code becomes:
def find_leaves(self):
if self.children:
for child in self.children:
yield from child.find_leaves()
else:
yield self
Note that in your base case, you return one node. That node then get propagated back in the recursion, and eventually returned. I.e. the node you are returned is the first one without children you hit. There are multiple ways to fix this issue (e.g. by returning and concatenating lists), but I think a natural way to do this is to make your function a generator. To do this, simply replace the return by a yield. Then, the return value of the function functions as an iterator, with each iteration yielding the next value that would be "returned" in execution until the function terminates.
You've not provided enough code for me to make a runnable and testable example but here's my guess of what you seek:
def find(self):
if not hasattr(self, 'children'):
return [self]
nodes = []
for child in self.children:
return nodes.extend(child.find())
return nodes
# ...
nodes = root.find()
print(nodes)
Just wondering what's the correct way of showing the next node in the __str__ method. See the following code and the results. It looks like there's some built-in recursive nature chaining the __str__ methods for each node in the list. Could anyone explain why and how I can make it behave normally, like only printing the next node's address. Thanks.
class Node(object):
def __init__(self, x):
self.val = x
self.next = None
def __str__(self):
return "Node {}, next is {}".format(self.val, self.next)
a = Node(1)
b = Node(2)
c = Node(3)
a.next = b
b.next = c
print(a)
Node 1, next is Node 2, next is Node 3, next is None
Instead of printing self.next's str representation, try printing its repr instead:
return "Node {}, next is {!r}".format(self.val, self.next)
Result:
Node 1, next is <__main__.Node object at 0x7f9f10066630>
The reason this is printing all the nodes is because .format convert each input into a string before concatenating it. To do this, it calls the __str__ method of the next node and the cycle continues.
There are two options to get it to act the way you want. In the __str__ method you can call the __repr__, !r, or you can get the memory location manually using id and format it any way you want.
return "Node {}, next is {!r}".format(self.val, self.next)
# or
return "Node {}, next is <__main__.Node object at {}>".format(self.val, hex(id(self.next)))
Both of these result in the same output:
Node 1, next is <__main__.Node object at 0x1005d7fd0>