Singly linked list is not reversing when using recursion - python

I'm having trouble figuring out what's missing. I've taken a several looks at other solutions online, and those don't seem to work when I apply the differences. I've spent a good amount of time trying to debug. Here's my code:
def recurse_reverse(self, curr, level):
print('-' * level, 'curr:', curr.value, '| next:', curr.next.value if curr.next else curr.next)
if (not curr) or (not curr.next): # if there's 0 or 1 node
return curr
# p = self.recurse_reverse(curr.next, level + 1)
self.recurse_reverse(curr.next, level + 1)
print('-' * level, 'curr:', curr.value, '->', curr.next.value, '->',
curr.next.next.value if curr.next.next else curr.next.next)
curr.next.next = curr
# checking if pointer moved
print('-' * level, 'curr:', curr.value, '->', curr.next.value, '->',
curr.next.next.value if curr.next.next else curr.next.next)
# curr.next = None
# return p
The output I get when I call
my_list = SinglyLinkedList()
my_list.add_to_tail(1)
my_list.add_to_tail(2)
my_list.add_to_tail(3)
my_list.add_to_tail(4)
print(my_list._head.value) # 1
print(my_list._head.next.value) # 2
print(my_list._head.next.next.value) # 3
print(my_list._head.next.next.next.value) # 4
my_list.recurse_reverse(my_list._head, 1)
is this:
- curr: 1 | next: 2
-- curr: 2 | next: 3
--- curr: 3 | next: 4
---- curr: 4 | next: None
--- curr: 3 -> 4 -> None
--- curr: 3 -> 4 -> 3
-- curr: 2 -> 3 -> 4
-- curr: 2 -> 3 -> 2
- curr: 1 -> 2 -> 3
- curr: 1 -> 2 -> 1
So printing at each level, it seems that the pointers are being moved correctly. However when I try to print the linked list's head and tail I call recurse_reverse, I get 1 and 3, respectively; yet, what I would expect is 4 and 1.
In many solutions I've seen, the last line of the code is curr.next = None, to remove the next pointer of the current node, but when include that in my code, I get AttributeError: 'NoneType' object has no attribute 'value'
I've also tried setting
p = self.recurse_reverse(curr.next, level + 1)
and then return p on the last line, but that doesn't work either.
Here's my implementation:
class _LinkNode:
def __init__(self, value):
self.value = value
self.next = None
class SinglyLinkedList:
def __init__(self):
self._head = None
self._tail = None
self._length = 0
def add_to_tail(self, value):
"""
Add a new node to the tail of the linked list.
Parameters
----------
value : int, float, string, dict, list, etc.
"""
new_node = _LinkNode(value)
if self._head is None: # if linked list is empty
self._head = new_node
if self._tail: # if linked list has a tail, i.e. > 1 node
self._tail.next = new_node
self._tail = new_node # regardless of current length, update tail
self._length += 1
def recurse_reverse(self, curr, level):
# see above

There are two issues with your code. First if list contains more than one element you don't swap _head.next, after recurse_reverse it will still point to second element of the original list and thus the last two elements of reversed list form a loop.The second issue is what you don't swap _head and _tail anywhere in your code.
Here's one way to to implement the reversal recursively:
#staticmethod
def reverse(prev, node):
# Recurse until end of the list
if node:
SinglyLinkedList.reverse(node, node.next)
node.next = prev
def recurse_reverse(self):
# Reverse nodes
SinglyLinkedList.reverse(None, self._head)
# Swap head & tail since they are reversed now
self._head, self._tail = self._tail, self._head

Related

Linked list recursive delete operation: How do I change the head?

class Node:
def __init__(self, val, next=None):
self.val = val
self.next = next
def rec_delete(head, val, prev=None):
if(head == None):
return False
if(head.val == val):
if(prev == None):
head = head.next
else:
prev.next = head.next
return True
return rec_delete(head.next, val, head)
head = Node(1, Node(2, Node(3, Node(4))))
rec_delete(head, 1)
rec_delete(head, 2)
rec_delete(head, 3)
rec_delete(head, 4)
Given a linked list 1 -> 2 -> 3 -> 4 I want to remove all the elements one by one but unsure how to assign a new head in python. The issue with my current code is since head goes through a function I cannot reassign the head. I want head to be None after all the operations.
Your delete function needs to return the head of the list after val has been deleted. This not only makes the implementation of the delete much simpler (the base case is the one where the head is itself being deleted), it is necessary for the caller to be able to handle the case where the head is deleted.
class Node:
def __init__(self, val, next=None):
self.val = val
self.next = next
def __repr__(self):
"""val1 -> val2 -> val3 ..."""
return f"{self.val}" + (f" -> {self.next}" if self.next else "")
def delete(head, val):
"""Delete val from list with given head, returning head of the modified list."""
if head.val == val:
# Deleting head, so the new head is head.next.
return head.next
# Head remains head, but we still need to delete val from head.next.
head.next = delete(head.next, val)
return head
head = Node(1, Node(2, Node(3, Node(4))))
print(head) # 1 -> 2 -> 3 -> 4
head = delete(head, 1)
head = delete(head, 2)
print(head) # 3 -> 4
head = delete(head, 3)
head = delete(head, 4)
print(head) # None

Reorder linked list in python

I tried to solve this problem. However, I have got time limit exceed.
Anyone can solve this problem without time limitation exceeds?
Here is the question.
You are given the head of a singly linked-list. The list can be represented as:
L0 → L1 → … → Ln - 1 → Ln
Reorder the list to be on the following form:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
You may not modify the values in the list's nodes. Only nodes themselves may be changed.
Here is my implementation. However, time limit exceeds.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
"""
Do not return anything, modify head in-place instead.
"""
temp=head
temp2=None
if temp.next is None:
pass
elif temp.next.next is None:
pass
else:
while temp.next:
temp2=temp
while temp2.next:
prev=temp2
temp2=temp2.next
temp2.next=temp.next
temp.next=temp2
prev.next=None
if temp2.next.next is None:
break
if temp2.next.next.next is None:
break
temp=temp.next.next
There is a "Discuss" section on LeetCode, where you could find some solutions and explanations.
One possible solution from there:
class Solution:
def reorderList(self, head: ListNode) -> None:
if not head:
return head
# find mid point
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# reverse the second half in-place
# slow.next: the start point of reverse
head2 = None
curr = slow.next
slow.next = None
while curr:
next = curr.next
curr.next = head2
head2 = curr
curr = next
# merge in-place
first, second = head, head2
while second:
first.next, first = second, first.next
second.next, second = first, second.next
return

Passing values between two doubly linked lists in python

I would like to pass a value of my doubly linked list into a new, empty doubly linked list so that I can work on sorting and removing nodes in DLL without changing my core one.
Here's how it looks like:
dllToBeSorted = DoublyLinkedList()
dllToBeSorted = newDoublyLinkedList #contents of newDoublyLinkedList here: 0 3 0 4 1 1 2 3
dllToBeSorted.sortList()
dllToBeSorted.removeDuplicates()
dllToBeSorted.display() #contents of dllToBeSorted here: 0 1 2 3 4
newDoublyLinkedList.display() #contents of newDoublyLinkedList here: 0 1 2 3 4
But when I try to display values, both of DLLs are the same. I don't know why this is happening, because this method works on usual variables (strings, ints and whatnot) but here for some reason it changes both of my DLLs.
I'm putting below my whole executable code:
class Node:
def __init__(self, data):
self.item = data
self.next = None
self.prev = None
class DoublyLinkedList:
def __init__(self):
self.head = None
self.start_node = None
def insertToEmptyList(self, data):
if self.start_node is None:
new_node = Node(data)
self.start_node = new_node
def insertToEnd(self, data):
if self.start_node is None:
new_node = Node(data)
self.start_node = new_node
return
n = self.start_node
while n.next is not None:
n = n.next
new_node = Node(data)
n.next = new_node
new_node.prev = n
def display(self):
if self.start_node is None:
print(" ")
return
else:
n = self.start_node
while n is not None:
print(n.item, end=" ")
n = n.next
print("\n")
def searchNode(self, data):
if self.start_node is None:
print("0")
return
else:
n = self.start_node
counter = 0
while n is not None:
if n.item == data:
counter += 1
n = n.next
print(counter)
def sortList(self):
if self.start_node is not None:
n = self.start_node
while n.next is not None:
index = n.next
while index is not None:
if n.item > index.item:
temp = n.item
n.item = index.item
index.item = temp
index = index.next
n = n.next
def removeDuplicates(self):
if self.start_node is not None:
n = self.start_node
while n is not None:
index = n.next
while index is not None:
if n.item == index.item:
temp = index
index.prev.next = index.next
if index.next is not None:
index.next.prev = index.prev
temp = None
index = index.next
n = n.next
newDoublyLinkedList = DoublyLinkedList()
newDoublyLinkedList.insertToEnd(0)
newDoublyLinkedList.insertToEnd(3)
newDoublyLinkedList.insertToEnd(0)
newDoublyLinkedList.insertToEnd(4)
newDoublyLinkedList.insertToEnd(1)
newDoublyLinkedList.insertToEnd(1)
newDoublyLinkedList.insertToEnd(2)
newDoublyLinkedList.insertToEnd(3)
dllToBeSorted = DoublyLinkedList()
dllToBeSorted = newDoublyLinkedList #contents of newDoublyLinkedList here: 0 3 0 4 1 1 2 3
dllToBeSorted.sortList()
dllToBeSorted.removeDuplicates()
dllToBeSorted.display() #contents of dllToBeSorted here: 0 1 2 3 4
newDoublyLinkedList.display() #contents of newDoublyLinkedList here: 0 1 2 3 4
This is because you are working on 2 different variables that point to the same data structure (https://www.geeksforgeeks.org/copy-python-deep-copy-shallow-copy/).
Overriding copy in DoublyLinkedList should do the trick.
def __copy__(self):
newList = DoublyLinkedList()
# add all data from self to newList
return newList
So your code would become:
dllToBeSorted = DoublyLinkedList()
dllToBeSorted = newDoublyLinkedList.copy() #contents of newDoublyLinkedList here: 0 3 0 4 1 1 2 3
dllToBeSorted.sortList()
dllToBeSorted.removeDuplicates()
dllToBeSorted.display() #contents of dllToBeSorted here: 0 1 2 3 4
newDoublyLinkedList.display() #contents of newDoublyLinkedList here: 0 1 2 3 4
To understand your problem you will have to understand this small example.
my_data = [1,2,3]
def my_func(data):
data.append(100)
return data
print("You sent: ", my_data)
# Calling my function to add an element to the list and returning the result
result_data = my_func(my_data)
print("I changed it to: ", result_data)
print("Your original input: ", my_data)
We didn't expected the list passed as an argument to get modified but it got modified. For detailed answer you can read this article
So basically you are performing all the sorting actions on your original lists/items behind the scenes and you are getting same outputs for your display functions.
You can use python lists copy function (or create an implementation for your class)
to make my example work as expected you can call the function my_func using
# result_data = my_func(my_data) Old way
result_data = my_func( my_data.copy() )
I would suggest a different approach.
Make your doubly linked list:
circular, including a sentinel node. This sentinel node can be instantiated by subclassing DoublyLinkedList from Node.
iterable, so to ease iteration, printing, searching without repeating code
to use native sorting as a solution (this is debatable, but at least it will be faster than bubble sort).
to return a new list when sorting, instead of sorting in-place
with a constructor that takes optional arguments, with which the list will be populated
Let the Node constructor take optional arguments for its prev/next attributes, and give it an isolate method which detaches it from its neighbors, closing the gap, so those neighbors become eachother's neighbors.
See what this approach brings to your code:
class Node:
def __init__(self, data=None, prev=None, nxt=None): # optional arguments
self.item = data
# Avoid that any next/prev becomes None. Should always reference a Node
self.next = nxt or self # default to referring to itself
self.prev = prev or self
# Make consistent
self.next.prev = self
self.prev.next = self
# Method to detach the node from its neighbors
def isolate(self):
self.prev.next = self.next
self.next.prev = self.prev
self.next = self.prev = self
# Subclassing makes the list a sentinel node.
# It does not count as a data node, but its next attribute will
# reference the first node (if any, else itself), and its prev
# attribute will reference the last node (if any, else itself)
class DoublyLinkedList(Node):
def __init__(self, *values):
super().__init__()
for data in values:
self.insertToEnd(data)
def insertToFront(self, data):
Node(data, self, self.next)
def insertToEnd(self, data):
Node(data, self.prev, self)
def __iter__(self): # This is very useful to have!!
node = self.next
while node is not self:
yield node.item
node = node.next
def __repr__(self):
return " ".join(map(repr, self))
def display(self): # Not really needed, as
print(self) # ... the list is printable
def search(self, data):
# Return the index of first occurrence, -1 if not found:
return next((i for i, item in enumerate(self) if item == data), -1)
def sorted(self):
return DoublyLinkedList(*sorted(self))
def removeDuplicates(self):
node = self.next
while node.next is not self:
if node.item == node.next.item:
node.next.isolate()
else:
node = node.next
myList = DoublyLinkedList(0, 3, 0, 4, 1, 1, 2 ,3)
print(myList)
mySorted = myList.sorted()
mySorted.removeDuplicates()
print(mySorted) # 1 2 3 4

Python Linked List Merge Sort not working

I have this assignment for uni that I have been working on for some time now, but I can't seem to figure out what's wrong. The goal is to merge two already-sorted linked lists into one sorted linked list.
The only function that I am allowed to change is the merge(a,b) function. I triple-checked, but I keep getting the following error:
in __repr__
out += str(node.weight)
AttributeError: 'ItemList' object has no attribute 'weight'
Is there anyone that can figure out what I should change? I'm very lost.
My code:
from __future__ import annotations
from typing import Optional
from dataclasses import dataclass
#dataclass
class Node:
weight: float
link: Optional[Node] = None
def merge(a: ItemList, b: ItemList) -> c:
"""
This function takes two linked lists, assumed sorted, and merges them
in-place, without creating new Nodes, just by changing the links.
"""
# create a new ItemList and dummynode as head:
c = ItemList()
c.head = Node(0)
# List1 is empty then return b
if a.head is None:
c._length += len(b)
b._length -= len(b)
return b
# if List2 is empty then return a
if b.head is None:
c._length += len(a)
a._length -= len(a)
return a
# If the weight of the element of a is smaller or equal to b's weight
if a.head.weight <= b.head.weight:
# We assign the weight of a's head to the head of c
c.head.weight = a.head.weight
#Give list a a new head (depletion) and subtract one from the length of a
a.head = a.head.link
a._length -= 1
# We will go through the algorithm again (recursively) to check for the element next-in-line.
c.head.link = merge(a, b)
# If the weight of the element of a is smaller or equal to b's weight
else:
# We assign the weight of a's head to the head of c
c.head.weight = b.head.weight
#Give list b a new head (depletion) and subtract one from the length of b
b.head = b.head.link
b._length -= 1
# We will go through the algorithm again (recursively) to check for the element next-in-line.
c.head.link = merge(a, b)
# return the merged list
return c
class ItemList:
head: Optional[Node]
def __init__(self):
"""Initialize an empty linked list for warehouse items."""
self.head = None
self._length = 0
def __len__(self) -> int:
return self._length
def insert(self, val: int) -> ItemList:
"""Insert a new item with given weight, at the beginning of the list"""
new_node = Node(weight=val, link=self.head)
self.head = new_node
self._length += 1
return self
def __repr__(self):
"""This function prints the list in a nice way."""
node = self.head
out = "["
while node is not None:
out += str(node.weight)
if node.link is not None:
out += ", "
node = node.link
out += "]"
return out
warehouse = (
ItemList()
.insert(8)
.insert(6)
.insert(4)
.insert(2)
.insert(0)
)
warehouse2 = (
ItemList()
.insert(9)
.insert(7)
.insert(5)
.insert(3)
.insert(1)
)
print(merge(warehouse,warehouse2))
You could write a recursive mergeNodes function:
def mergeNodes(a: Node, b: Node):
"""
This function takes two linked lists of Nodes, assumed sorted, and
merges them into a single linked list of Nodes, in-place, without
creating new Nodes, just by changing the links.
"""
if a is None:
return b
if b is None:
return a
if a.head.weight <= b.head.weight:
a.link = mergeNodes(a.link, b)
return a
else
b.link = mergeNodes(a, b.link)
return b
Merging the lists becomes very simple:
def merge(a: ItemList, b: ItemList) -> ItemList:
"""
This function takes two ItemList objects, assumed sorted, and merges them
into a new ItemList, without creating new Nodes, just by changing the links.
the original objects are destroyed
"""
# create a new ItemList:
c = ItemList()
c._length = a._length + b._length;
c.head = mergeNodes(a.head, b.head)
# clear the original lists because the links are no longer consistent
a._length = 0
a.head = None
b._length = 0
b.head = None
return c
Note that merge should be a method of class ItemList and merge a list passed as an argument into self.

How do you create a LinkedList which contains loops?

I'm trying to come up with a test case for the solution given by hackerrank for detecting cycles in a linkedlist with python. The solution given by hackerrank is:
class Node(object):
def __init__(self, data = None, next_node = None):
self.data = data
self.next = next_node
def has_cycle(head):
fast = head;
while(fast != None and fast.next != None):
fast = fast.next.next;
head = head.next;
if(head == fast):
return True;
return False;
So I created what I thought was the following LinkedList
8 -> 7 -> 6 -> 5 -> 4 -> 3
^ |
| V
1 <-----------------2
Using this code:
Node_1 = Node(1)
Node_2 = Node(2, Node_1)
Node_3 = Node(3, Node_2)
Node_4 = Node(4, Node_3)
Node_5 = Node(5, Node_4)
Node_6 = Node(6, Node_5)
Node_7 = Node(7, Node_6)
Node_8 = Node(8, Node_7)
Node_1 = Node(1, Node_7)
But the results weren't what I expected:
print(has_cycle(Node_8)) # returns False
print(Node_2.next.next) # returns None
print(Node_1.next.data) # returns 7
This would work in C++, so it makes me think the problem is that I'm passing copies of objects rather than their pointers. If that's the case could someone point me to some material to learn about those sorts of concepts, please?
Also, how would I create the above mentioned test case?
Thanks!
The line:
Node_1 = Node(1, Node_7)
is creating a new node, it's not modifying the original Node_1 that's linked from Node_2. The lists you've created look like:
8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1
^
1 ---|
To create a cycle, you need a way to modify the next link of an existing node. Add this method to the Node class:
def set_next(self, next_node = None):
self.next = next_node
Then replace the last line with:
Node_1.set_next(Node_7)

Categories