reverse linked lists recursively - python

I have a problem with an algorithm which reverses Linked Lists recursively in Python.
def reverse(lis, prev = None):
if lis.next != None:
reverse(lis.next, lis)
lis.next = prev
Input: 3 1 4
Output: 3
Any idea why its not working?

The problem is that the root of your linked list has changed as well: after altering the list, your element 3 is indeed the last element, your function better returns the root as well:
def reverse(lis, prev = None):
if lis.next != None:
temp = lis.next
lis.next = prev
return reverse(temp, lis)
else:
return lis # the new root
so now you call it with:
oldroot = ... #construct linked list
newroot = reverse(oldroot)
print(newroot)
So your function is correct, but you have the wrong linked list element in your hands after the operation.

This looks like some functional programming exercise, so I present you a functional solution (it copies the list):
def reverse(link, rest=None):
if link is None:
return rest
return reverse(link.next, LinkedList(link.value, rest))
class LinkedList:
def __init__(self, value=None, next=None):
self.next = next
self.value = value
def print(self):
print(self.value)
if self.next is not None:
self.next.print()
def to_linked_list(items):
if len(items) == 0:
return None
return LinkedList(items[0], to_linked_list(items[1:]))
to_linked_list([1, 2, 3, 4, 5]).print()
# 1, 2, 3, 4, 5
reverse(to_linked_list([1, 2, 3, 4, 5])).print()
# 5, 4, 3, 2, 1

In place instead of creating a new chain:
def reverse_linked_list(node, prev=None):
if node.next is None:
node.next = prev
return node
root_node = reverse_linked_list(node.next, node)
node.next = prev
return root_node

Related

Want to create a program to scale a linked list by a certain factor

I want to write a function where you input a linked list and a factor, and the function returns a new linked list scaled by that factor. For example:
scale(linkify([1, 2, 3]), 2)
2 -> 4 -> 6 -> None
First, I made a function that, when you input a list of items, converts into a linked list. This is it here:
def linkify(item: list[int]) -> Optional[Node]:
"""Return a Linked List of Nodes with same values and same order as input list."""
if len(item) == 0:
return None
elif len(item) == 1:
return Node(item[0], None)
else:
return Node(item[0], linkify(item[1:]))
Now, I'm trying to write the function in order to scale that list.
Here is what I have now for the function:
def scale(head: Optional[Node], factor: int) -> Optional[Node]:
"""Returns new linked list of nodes where each value in original list is scaled by scaling factor."""
if head is None:
return None
else:
return Node(head.data * factor, scale(head.next, factor))
However, when I try to test this, I get an error saying exercises.ex11.linked_list.Node object at 0x0000013392C97C10>, and I'm not entirely sure what this means. Can anyone tell me what I'm getting wrong?
Also, I have to create the function recursively, and can't use any other functions outside the ones I've created.
Here is the test case I created as well:
def test_scale_factor() -> None:
linked_list: list[int] = [1, 2, 3]
linked_list_2: list[int] = [2, 4, 6]
assert is_equal(scale(linkify(linked_list), 2), linkify(linked_list_2))
Thanks for your help!
Is this a proper solution for your case?
I always try to avoid using recursions.
Sure you also can add some checks on the function inputs.
class Node:
def __init__(self, data = None):
self.data = data
self.next = None
class Linkedlist:
def __init__(self):
self.head = None
def linkify(self, nodes):
if len(nodes) == 0:
return
self.head = node = Node()
for i in nodes:
node.next = Node(i)
node = node.next
self.head= self.head.next
def is_equal(self, other_llist):
node1 = self.head
node2 = other_llist.head
while node1 and node2:
if node1.data != node2.data:
return False
node1 = node1.next
node2 = node2.next
if node1 or node2:
return False
return True
def scale(self, factor):
node = self.head
while node:
node.data *= factor
node = node.next
llist1 = Linkedlist()
llist1.linkify([1, 2, 3])
llist1.scale(2)
llist2 = Linkedlist()
llist2.linkify([2, 4, 6])
print(llist1.is_equal(llist2))

Filtering nodes in a DLL

Here is what I have so far, I want to create the function that would remove elements lower than a specified value in the doubly linked list or above the specified value.
class DoublyLinkedList:
class Node:
""" Nodes in list """
def __init__(self, element, prev=None, next_node=None):
self.element = element
self.prev = prev
self.next = next_node
def __init__(self):
self._header = self.Node(None, None, None)
self._trailer = self.Node(None, None, None)
self._header.next = self._trailer
self._trailer.prev = self._header
self._size = 0
def append(self, element):
:arg element: the value to add to this list
new_node = self.Node(element, self._trailer.prev, self._trailer)
self._trailer.prev.next = new_node
self._trailer.prev = new_node
self._size += 1
def __len__(self):
return self._size
def __str__(self):
# Python's list is used here to avoid string concatenation
result = []
current = self._header.next
for i in range(len(self)):
result.append(current.element)
current = current.next
return str(result)
def remove(self, low, high):
for element in self:
try:
if element.next < low:
element.next = element.next.next
elif element.next > high:
element.next = element.next.next
except Exception as e:
print('low must be less than or equal to high')
pass
^^ That is what I've tried so far ^^
here is how I wanted it to work:
I'm not sure how to get it to filter out the higher or lower values
DoublyLinkedList.append(5)
DoublyLinkedList.append(7)
DoublyLinkedList.append(8)
DoublyLinkedList.append(3)
DoublyLinkedList.append(9)
[5, 7, 8, 3, 9]
DoublyLinkedList.remove(5,8)
its output should be:
[5, 7, 8]
Some issues in your code:
append has a line that should be a comment (starting with #)
for element in self: will not work, as self is not iterable. Also, it is rarely a good idea to iterate over a collection with a for loop when you are planning to remove items from that same collection. It will be better to use a form of iteration that you have already used in the __str__ method.
element.next < low is comparing two different types: .next is a node, while low is a number. Calling that loop variable element is confusing, as that is also the name of the attribute of your nodes. You want to have something like current.next.element < low.
If the above is corrected, there is no reason why the try block should trigger an exception, let be that it has to do with how low and high relate to eachother. If you want to output a message when high > low, then don't do that in the loop, but just test for that condition before starting the loop.
When you remove one node, you should also decrease the list size.
You can avoid code repetition by using one if to compare with low and high in one expression, using operator chaining
DoublyLinkedList is the class, but a list should be an instance of that class. So you need to first create that instance, and then call the methods on that instance, not on the class.
Here is a correction of your code:
class DoublyLinkedList:
class Node:
""" Nodes in list """
def __init__(self, element, prev=None, next_node=None):
self.element = element
self.prev = prev
self.next = next_node
def __init__(self):
self._header = self.Node(None, None, None)
self._trailer = self.Node(None, None, None)
self._header.next = self._trailer
self._trailer.prev = self._header
self._size = 0
def append(self, element):
# :arg element: the value to add to this list
new_node = self.Node(element, self._trailer.prev, self._trailer)
self._trailer.prev.next = new_node
self._trailer.prev = new_node
self._size += 1
def __len__(self):
return self._size
def __str__(self):
result = []
current = self._header.next
for i in range(len(self)):
result.append(current.element)
current = current.next
return str(result)
def remove(self, low, high):
# Perform the check before the loop
if low > high:
print('low must be less than or equal to high')
return
# Iterate the nodes like in __str__, but start one node earlier
current = self._header
for i in range(len(self)):
# Don't compare the node, but its element.
# Use chained comparison;
if low <= current.next.element <= high:
# Only move to next node when not removing
current = current.next
else:
current.next = current.next.next
self._size -= 1 # Also reduce size
# Should create an instance and work with that
lst = DoublyLinkedList()
lst.append(5)
lst.append(7)
lst.append(8)
lst.append(3)
lst.append(9)
print(lst) # [5, 7, 8, 3, 9]
lst.remove(5,8)
print(lst) # [5, 7, 8]

Making a function to clone a linked list without using the copy function

class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
self.size = 0
# find a node which contains the data matching the input value
def findNode(self, value):
curr = self.head
while curr:
if curr.data == value:
return curr
curr = curr.next
return curr
# add a new node as the first node
def addNode(self, data):
newNode = Node(data) # create the new node
newNode.next = self.head
self.head = newNode
self.size+=1
# print the data in all the nodes
def printNode(self):
listprint = []
curr = self.head
while curr:
listprint.append(curr.data)
curr = curr.next
print(listprint)
#what i am trying to make
def duplicateLL(original):
newlist = LinkedList()
curr = original
if not curr:
return
while curr != None:
newlist.addNode(curr)
curr = curr.next
return newlist
#to test if function works
LLA = LinkedList()
for n in range(1,5):
LLA.addNode(n)
LLB = duplicateLL(LLA)
# modify LLA
curr = LLA.head
for n in range(LLA.size):
curr.data += 10
curr = curr.next
LLA.printNode()
LLB.printNode()
# driver code will be executed if this file is executed directly
# driver code will not be executed if this file is imported into another file
if __name__ == "__main__":
mylinkedlist = LinkedList()
for i in range(5):
mylinkedlist.addNode(2*i)
mylinkedlist.printNode()
I am trying to make a function duplicateLL, which is outside of the linked list class. Function duplicateLL is supposed to clone a linked list without using the built in copy function.
**** Note that I can only edit the function duplicateLL.***
The code prompts out: 'LinkedList' object has no attribute 'next'.
What am I doing wrong?
The outputs should be:
[14, 13, 12, 11]
[4, 3, 2, 1]
[8, 6, 4, 2, 0]
There was two problem for the duplicateLL function.
First, you tried to search through LinkedList which has no next attribute. Therefore, you need to use Node to travel through each element, so I assigned Node attribute to curr variable in duplicateLL function.
Second, you need to add the data of the node with the addNode function. I changed that part as well.
Here is the code:
def duplicateLL(original : LinkedList):
newlist = LinkedList()
curr = original.head
if not curr:
return
while curr != None:
newlist.addNode(curr.data)
curr = curr.next
return newlist.reverse()

Print stack or row in different ways with same function in Python

My task is to print Stack and Queues in different ways.
I implemented Stack with single linked list and Queue with double linked list.
This is main function where I push elements into Stog(Stack) and Red(Queue).
stavi function is same as enqueue, this is in Queue class.
def stavi(self, data):
if self.head == None:
self.head = Node(data)
else:
newnode = Node(data)
newnode.next = self.head
self.head = newnode
I cannot change main part!
L = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
S = Stog()
R = Red()
for element in L:
R.stavi(element)
This is how I call function ispis(print):
ispis(S)
ispis(R)
And this is how I made function ispis:
def ispis(self):
current = self.head
if (self.head == None):
print ("Lista je prazna")
return
print("<-- ", end= "")
while(current != None):
if (current.next is None):
print(current.data)
else:
print (current.data, end = ", ")
current = current.next
return
Result should be:
ispis(S) <-- 9 6 14 22 5 5
ispis(R) # 10 9 8 7 6 5 4 3 2 1 -->
My results are :
<-- 9, 6, 14, 22, 5, 5
<-- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
I know how to made it for Stack, but how can my code "understand" difference between Stack and Queue. Or should I somehow add elements in Queue from tail ?
Some issues in the information provided:
There is no need to implement a queue with a doubly linked list. A singly linked list is all you need, but with a reference to both its head and its tail.
Your code only populates the queue, not the stack
It makes no sense to expect "9 6 14 22 5 5" as output when the input is "1, 2, 3, 4, 5, 6, 7, 8, 9, 10"
It is not best practice to have a print method. Printing is a concern that should be kept separate from stack/queue implementation logic. Instead define a method that iterates the values in the list -- for instance by implementing __iter__, and if you need a formatted string, __str__ or __repr__.
If a function is to behave differently for instances of different classes, then it is better practice to define it on each class separately.
Here is an idea of an implementation, where Queue inherits from Stack. As I don't understand the logic for printing the "#" symbol, I will use "-->" instead, such that an arrow towards the list indicates the side where future values will be added, and an arrow away from the list indicates the side where values will be removed from the list.
class Node:
def __init__(self, value, nxt=None):
self.value = value
self.next = nxt
class Stack:
def __init__(self, values=None):
self.head = None
if values:
self.insert(*values)
def insert(self, *values):
for value in values:
self.head = Node(value, self.head)
def extract(self):
if self.head:
value = self.head.value
self.head = self.head.next
return value
def __iter__(self):
node = self.head
while node:
yield node.value
node = node.next
def join(self):
return " ".join(map(str, self)) or "(empty)"
def __str__(self):
return "<--> " + self.join()
class Queue(Stack):
def __init__(self, values=None):
self.tail = None
super().__init__(values)
def insert(self, *values):
if values and not self.tail:
self.head = self.tail = Node(values[0])
values = values[1:]
for value in values:
self.tail.next = self.tail = Node(value)
def __str__(self):
return "<-- " + self.join() + " <--"
values = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
s = Stack(values)
q = Queue(values)
print("Stack", s)
print("Queue", q)
As Queue inherits from Stack the methods to add/remove values are called insert and extract for both classes. Queue overrides the Stack implementation of insert to change the FILO into FIFO logic.

Linked Lists: How to remove odd numbers?

I am currently taking an online computer science introductory course and have just learned the concept of a linked list. Though I understand the concept of linked lists, I still am unsure as how to deal with linked lists.
As such, I seek out help in solving the following problem, which will be of significant help for my understanding of linked lists:
Write a function (not in LinkedList class definition) that given a linked list, will change that linked list to filter out odd numbers. Immediately after the function returns, the linked list will only have even numbers.
I am unsure as to how to access the nodes in the list and check whether they are odd or even and remove or keep them accordingly.
I apologize if this seems like a trivial question, but I would appreciate any help that might help me learn.
The code for the linked list and node classes (as provided by the online course):
class Node:
def __init__(self, data=None, next_node=None):
self.data = data
self.next = next_node
def __str__(self):
return str(self.data)
class LinkedList:
def __init__(self):
self.length = 0
self.head = None
def print_list(self):
node = self.head
while node is not None:
print(node, end=' ')
node = node.next
print('')
def add_at_head(self, node):
node.next = self.head
self.head = node
self.length += 1
def remove_node_after(self, node):
if node.next is not None:
temp = node.next
node.next = node.next.next
temp.next = None
self.length -= 1
def remove_first_node(self):
if self.head is None:
return
temp = self.head
self.head = self.head.next
temp.next = None
self.length -= 1
def print_backward(self):
def print_nodes_backward(node):
if node.next is not None:
print_nodes_backward(node.next)
if node is not None:
print(node, end=' ')
if self.head is not None:
print_nodes_backward(self.head)
print('')
Let's say you have a bare-bones simple linked list that looks like this:
class LinkedList:
class ListNode:
def __init__(self, data):
self.data = data
self.next = None
def __init__(self):
self.head = None
def add(self, data):
if self.head is None:
self.head = LinkedList.ListNode(data)
else:
current_node = self.head
while current_node.next is not None:
current_node = current_node.next
current_node.next = LinkedList.ListNode(data)
def __str__(self):
ret = "["
current_node = self.head
while current_node is not None:
ret = ret + str(current_node.data)
if current_node.next is not None:
ret = ret + ", "
current_node = current_node.next
ret = ret + "]"
return ret
In other words, the LinkedList contains a single head, which is a ListNode. Every element in the Linked List is contained in a ListNode, and each ListNode points towards the next element in the list.
As you can see, for adding an element to the list, we either create a node at the head if the list is empty (i.e. self.head is None), or we traverse to the end of the list by continuously jumping to the .next element for each ListNode, starting from the head. We also use this paradigm for printing a string representation of our list.
So, to remove any node from the linked list, we can simply change the node that references it, so that the node we want to remove gets skipped. At which point it will disappear.
To remove all list nodes containing odd-numbered data, we might do something like this:
def remove_odds(self):
# special case: head node
# remove odd head elements by simply setting head to the next element after
while (self.head is not None) and (self.head.data % 2 == 1):
self.head = self.head.next
# regular case: the rest of the nodes
current_node = self.head
while (current_node is not None) and (current_node.next is not None):
# if the next node's data is odd, then
if current_node.next.data % 2 == 1:
# skip that node by pointing this node's .next to the next node's .next
current_node.next = current_node.next.next
# otherwise, move forwards in the list
else:
current_node = current_node.next
Proof of concept:
>>> lst = LinkedList()
>>> lst.add(2)
>>> lst.add(5)
>>> lst.add(6)
>>> lst.add(3)
>>> lst.add(7)
>>> lst.add(8)
>>> lst.add(10)
>>> lst.add(1)
>>> lst.add(4)
>>> print(lst)
[2, 5, 6, 3, 7, 8, 10, 1, 4]
>>> lst.remove_odds()
>>> print(lst)
[2, 6, 8, 10, 4]
Copied from comment: The idea is to iterate through the list head-to-tail while remembering the previous node; when you find a garbage node, apply remove_node_after to the remembered node, or move the head to the current node if we haven't had time to remember anything yet.
The code would be something like this (untested):
class LinkedList:
# ...
def delete_if(self, pred):
prev = None
curr = self.head
while curr:
if pred(curr.data):
if prev:
self.remove_node_after(prev)
else:
self.head = curr
prev = curr
curr = curr.next
llist.delete_if(lambda x: x % 2 == 1) # delete if odd
# Mahmoud AL-Mokdad
# this course on Udemy By SEfactoru right๐Ÿ˜
# use my code ๐Ÿ˜‰
def filter_even(ll):
first_node = ll.head
while (first_node is not None) and (first_node.data % 2 != 0):
ll.remove_first_node()
first_node = ll.head
node = first_node
while node is not None and node.next is not None:
if node.next.data % 2 != 0:
ll.remove_node_after(node)
else:
node = node.next

Categories