Python 3 - Using recursion in a linked list - python

Python 3 - I am new to coding and am finding recursion difficult. I'm making a linked list class with recursive methods for adding and removing items from the list. Right now, I am unable to remove an item if it happens to be the first item in my list. I wrote some alternative code which could remove the first item from the list if I included another parameter (previous) and another base case, but then I could only remove the first item and spent way too long trying to figure out why so I scrapped that entirely. I would appreciate a hint!
Also, I am already aware that I have getters and am not using them properly.
class Node:
"""
Represents a node in a linked list
"""
def __init__(self, data):
self._data = data
self._next = None
def get_data(self):
"""getter method for data in Node class"""
return self._data
def get_next(self):
"""getter method for next in Node class"""
return self._next
class LinkedList:
"""
A linked list implementation of the List ADT
"""
def __init__(self):
self._head = None
def get_head(self):
"""getter function for head of list"""
return self._head
def add(self, val):
""" Adds a node containing val to the linked list - helper function"""
self._head = self.recursive_add(self._head, val)
def recursive_add(self, node1, val):
""" Adds a node containing val to the linked list """
if node1 is None:
return Node(val)
else:
node1._next = self.recursive_add(node1._next, val)
return node1
def remove(self, val):
"""removed the node containing val from the linked list - helper function"""
self.recursive_remove(self._head, val)
def recursive_remove(self, node1, val):
"""
Removes the node containing val from the linked list
"""
if node1 is None:
return node1
elif node1._data == val:
return node1._next
else:
node1._next = self.recursive_remove(node1._next, val)
return node1
def main():
my_list = LinkedList()
my_list.add(13)
my_list.add(9)
my_list.add(5)
my_list.remove(9)
if __name__ == '__main__':
main()

def remove(self, val):
"""removed the node containing val from the linked list - helper function"""
if self._head and self._head._data == val:
self._head = self._head._next
return
self.recursive_remove(self._head, val)
if its at the start, the head needs to be changed.

In remove you call recursive_remove, but ignore its return value. You should use it as the (potentially different) _head reference, must like is done in the recursive method itself, where you have:
node1._next = self.recursive_remove(node1._next, val)
# ^ │
# └───────────────────────────────────┘
Note how node1._next is passed as argument, and the method's return value is the (potentially different) reference that node1._next should end up with. The same pattern should be applied in the initial call in remove:
def remove(self, val):
self._head = self.recursive_remove(self._head, val)
# ^ │
# └──────────────────────────────────┘
NB: the same pattern is used in add, where you do it correctly.

Related

Calculating no. of nodes in a Binary Tree in O(1)?

I am taking Data Structures and Algorithm course from Jovian. Currently on Binary Tress, but I am stuck on one question where we need to calculate no. of nodes in O(1) time.
Firstly here's how the final class TreeMap looks like:
class TreeMap:
def __init__(self):
self.root = None
def __setitem__(self, key, value):
node = find(self.root, key)
if not node:
self.root = insert(self.root, key, value)
self.root = balance_tree(self.root)
else:
update(self.root, key, value)
def __getitem__(self, key):
node = find(self.root, key)
return node.value if node else None
def __iter__(self):
return (x for x in list_all(self.root))
def __len__(self):
return size(self.root)
def display(self):
return display_keys(self.root):
Currently, it's calculating no. of nodes with the recursion method. I think we just need a counter and increment every time a node is created and we also have a hint to modify the BSTNode class. So this is how I did it:
class BSTNode:
counter = 0
def __init__(self, key, value=None):
self.key = key
self.value = value
self.left = None
self.right = None
self.parent = None
BSTNode.counter += 1
But I don't know how do I implement or use the counter in __len__ function in class TreeMap. Any help would be much appreciated.
Here is the link to Jovian Lesson: https://jovian.ai/learn/data-structures-and-algorithms-in-python/lesson/lesson-2-binary-search-trees-traversals-and-balancing

Implementing an Unbounded Min Priority Queue using a Singly Linked List in Python

I am doing a class project and I have to implement an unbounded singly linked list using python. I need to implement a given 'insert' that has to consider the following cases...
insert into an empty queue
insert at the front of the queue
insert after some existing node.
This is the class given in python and i need to implement the insert function given at the bottom.
I am new to this so any help would be greatly appreciated!
class CinemaPriorityQueue:
"""A linked list implementation of an unbounded min-priority queue."""
class Node:
"""A node in a linked list."""
def __init__(self, item: object, priority_value: int) -> None:
"""Initialise the node with the given item and priority value."""
self.item = item
self.priority = priority_value
self.next = None
def __init__(self) -> None:
"""Initialise the queue to be empty."""
self.head = None
def is_empty(self) -> bool:
"""
Preconditions: true
Postconditions: the output is true if the queue is empty, false otherwise
"""
return self.head == None
def print(self) -> None:
"""Print out the queue"""
if self.head == None:
print('The queue is empty')
else:
current = self.head
while current != None:
print(current.item, current.priority)
current = current.next
def insert(self, item: object, priority_value: int) -> None:
"""Insert item according to priority.
Preconditions: true
Postconditions: post-self is the sequence
pre-self with item inserted after
the last item in self with the same priority
"""
pass
#*Write your code solution here*
I can tell you the idea behind the solution. In the singly linked list, while adding a new element, compare it with the elements in the linked list and add it where it fits so that the linked list is sorted. This will give you a min priority queue.

how to print <__main__.LinkedList object at 0x01BA8A90>? [duplicate]

This question already has answers here:
How to print instances of a class using print()?
(12 answers)
Closed 3 years ago.
can anybody explain why it is showing me and how to print it?
what am I doing wrong and how to print this object reference?
i have also tried printing new_list(inside sort() ) but still the same
I am printing list then why it is not showing
I know some of the people asked before about related to this...but still I didn't get it.
class node(object):
def __init__(self, d, n=None):
self.data=d
self.next_node=n
def get_data(self):
return self.data
def set_data(self,d):
self.data=d
def get_next(self):
return self.next_node
def set_next(self, n):
self.next_node=n
def has_next(self):
if self.get_next() is not None:
return True
else:
False
class LinkedList(object):
def __init__(self, r=None):
self.root=r
self.size=0
def get_size(self):
return self.size
def add(self,d):
new_node = node(d, self.root)
self.root = new_node
self.size+=1
def sort(self):
if self.size>2:
newlist = []
this_node = self.root
newlist.append(this_node)
while this_node.has_next():
this_node = this_node.get_next()
newlist.append(this_node)
newlist = sorted(newlist ,key = lambda node: node.data,reverse=True)
newLinkedList = LinkedList()
for element in newlist:
newLinkedList.add(element)
return newLinkedList
return self
new_list=LinkedList()
new_list.add(10)
new_list.add(20)
new_list.add(30)
new_list.sort()
i expected that it will print list print a list
but it is showing <main.LinkedList object at 0x00E20BB0>
how to print this object ?
You are not printing out node values of the linked list instead you are printing out the return value of the sort() function which is an object of the class LinkedList.
If you want to print the linked list, you have to traverse the list and print out each node value individually.
Here is the recursive solution of how you can print a linked list.
def print_list(head):
if head != null:
print(head.val)
print_list(head.next)
You can call this method after calling the sort function

Reversing Single Linked list using Recursion

I have made a function that reverses the singly linked list using Recursive method.
However I am having some difficulty executing my below code:
class node:
def __init__(self,data=None):
self.next=None
self.data=data
class linked_list:
def __init__(self):
self.head=node()
def append(self,data):
new_node=node(data)
cur_node=self.head
while (cur_node.next!=None):
cur_node=cur_node.next
cur_node.next=new_node
return cur_node.data
def display(self):
elements=[]
cur_node=self.head
while(cur_node.next!=None):
cur_node=cur_node.next
elements.append(cur_node.data)
print(elements)
def reverseRecursive(self,prev_code,cur_node):
if cur_node.next!=None:
reverseRecursive(cur_node,cur_node.next)
cur_node.next=prev_node
else:
self.head=cur_node
return
lst1=linked_list()
lst1.display()
lst1.append(1)
lst1.append(3)
lst1.append(5)
lst1.append(7)
lst1.display()
lst1.reverseRecursive(None,_____)
lst1.display()
What should I pass the second argument in reverseRecursive function/method so that I can execute it?
As a second argument, I want to simply pass the head node of a linked list. But I don't know how to get the head node from the init method of the class linked_list
I have tried several things but I'm not able to resolve it. Maybe I am not very good at OOP concepts. Can anyone please help me in resolving this?
The linked list is a recursive structure, and so using it with functional style will yield the best results. In your program, you have implemented a linked list using procedural style and mutable nodes – you change the values of data and next over time. While this might feel like an intuitive approach, I'd like to focus on an immutable discipline that frees us from crippling state complexity.
First, we fix the node constructor function. We set both properties when constructing new nodes because they will not change later in the program –
class node:
def __init__ (self, data, next):
self.data = data
self.next = next
Then a linked_list is just a single node constructed by a particular convention:
node.data hold's the node's data
node.next is either:
another linked_list
or, None, representing the end of the list
We begin with the constructor for linked_list –
class linked_list:
def __init__ (self, node = None):
self.node = node
# ...
And implement is_empty, head, and tail properties to abstract away node –
class linked_list:
def __init__ (self, node = None):
self.node = node
#property
def is_empty (self):
return self.node is None
#property
def head (self):
if self.is_empty:
raise Exception ("cannot get head of an empty list")
else:
return self.node.data
#property
def tail (self):
if self.is_empty:
raise Exception ("cannot get tail of an empty list")
else:
return self.node.next
Now the use of a node is completely abstracted, and we can write higher level list behaviors by using our new properties –
class linked_list:
...
def length (self):
if self.is_empty:
return 0
else:
return 1 + self.tail.length()
Above, we see it's very easy to talk about our list through use of its properties. Before we go further, let's see how we can construct lists and visualize them using print. For object-to-string conversion, we use __str__ –
class linked_list:
...
def add (self, x):
return linked_list (node (x, self))
def __str__ (self):
if self.is_empty:
return "None"
else:
return str (self.head) + " -> " + str (self.tail)
ls = linked_list().add('A').add('B').add('C')
print (ls)
# C -> B -> A -> None
print (ls.length())
# 3
Remember, because we've built an immutable linked list, add does not change the list it was called upon –
ls = linked_list().add('A').add('B').add('C')
print (ls)
# C -> B -> A -> None
print (ls.add('D'))
# D -> C -> B -> A -> None
print (ls)
# C -> B -> A -> None
Finally, we can implement reverse –
class linked_list:
# ...
def reverse (self):
def loop (ls, acc):
if ls.is_empty:
return acc
else:
return loop (ls.tail, acc.add(ls.head))
return loop (self, linked_list())
ls = linked_list().add('A').add('B').add('C')
print (ls)
# C -> B -> A -> None
print (ls.reverse())
# A -> B -> C -> None
Reversing the list does not mutate it
print (ls)
# C -> B -> A -> None
print (ls.reverse())
# A -> B -> C -> None
print (ls)
# C -> B -> A -> None

__next__ and next with arguments

I'm writing an implementation of doubly linked lists. In order to traverse the list, I'm using something like:
class Node:
""" A node in our linked list """
def __init__(self, value: Any, next: Union['Node', None] =None,
previous: Union['Node', None] =None) -> None:
self.value = value
self.next = next
self.previous = previous
...
def __next__(self, direction: int =1) -> Union['Node', None]:
if direction == 1:
return self.get_next()
else:
return self.get_previous()
...
where get_next and get_previous are just getters of self.next and self.previous.
However, PyCharm yells at me for trying to call next as
next(some_node, direction=-1). What's the proper way to do this?
Besides __iter__ there is also __reversed__. Both are required to return iterators. The __next__ method should be implemented on iterators (not on node-classes). Note that all magic methods (when called by a function like next instead of directly invoked) need to implement the expected arguments not more - not less.
For example a doubly linked list could just implement __iter__ and __reversed__ and rely on next and previous attribute of the Node:
class Node(object):
def __init__(self, val, nxt, prv):
self.val = val
self.nxt = nxt
self.prv = prv
class DoublyLinkedList(object):
def __init__(self, base=None, last=None):
self.base = base
self.last = last
def prepend(self, val):
new = Node(val, self.base, None)
if self.base is None:
self.base = new
self.last = new
else:
self.base.prv = new
self.base = new
def append(self, val):
new = Node(val, None, self.last)
if self.last is None:
self.base = new
self.last = new
else:
self.last.nxt = new
self.last = new
def __iter__(self):
current = self.base
while current is not None:
yield current
current = current.nxt
def __reversed__(self):
current = self.last
while current is not None:
yield current
current = current.prv
For example:
dl = DoublyLinkedList()
dl.prepend(10)
dl.prepend(20)
dl.prepend(30)
for i in dl:
print(i.val)
gives:
30
20
10
similar for reversed:
for i in reversed(dl):
print(i.val)
# prints:
10
20
30
__next__ is part of the iterator protocol and should be used as described in said protocol, doing otherwise only make problems with the rest python.
In your case just rename the function to simple next and use as some_node.next(-1), though I would change the direction argument to a boolean, as that is how you use it, and its name too. Like this for example
class None:
...
def next(self, forward:bool=True) -> Union['Node', None]:
if forward:
return self.get_next()
else:
return self.get_previous()
and use as some_node.next(), some_node.next(False) or even some_node.next(0) (using 0 instead of False for the same effect)
The extra argument to next is a default value, and __next__ doesn't take any extra arguments. Python doesn't have any sort of two-way iterators. If your interface is not exactly the same as for i in obj:, then you should write your own.

Categories