How to remove last node of a linked list (Python) - python

The goal is to remove the last node of a linked list. (Python) The code that I have written only added to the list instead of taking the tail away. I'm not sure if the error is coming from this part here self.head.next.prev = None. I tried refining that part but another error would occurred. What I have posted is only small section of the code.
def remove_tail(self):
if self.tail == self.head:
self.head = None
self.tail = None
elif self.head is not None:
self.head.next.prev = None
self.tail = self.head.next
print("\n=========== PROBLEM 2 TESTS ===========")
ll.remove_tail()
print(ll) # linkedlist[5, 4, 3, 2, 2, 2, 1, 0]
ll.remove_tail()
print(ll) # linkedlist[5, 4, 3, 2, 2, 2, 1]
I reverse the names of head and tail to see of that was causing the issues.
elif self.head is not None:
self.head.next.prev = None
self.tail = self.head.next
Here is the updated code. Errors still occur when I run it.
def remove_tail(self):
if self.tail == self.head:
self.head = None
self.tail = None
if self.tail is not None:
self.tail = self.tail.prev
self.tail.next = None

Think about the structure of the list (assuming you have more than one element; your code already handles that special case):
head tail
+---+->+---+->+---+->+---+->None
| 1 | | 2 | | 3 | | 4 |
+---+<-+---+<-+---+<-+---+
If you remove the last element, you're going to have:
head tail
+---+->+---+->+---+->None
| 1 | | 2 | | 3 |
+---+<-+---+<-+---+
What needs to change in order for that to happen?
tail needs to be set to the second-to-last node (3, aka tail.prev)
the new tail's next pointer needs to be set to None (since the old tail, 4, is no longer part of the list)
So instead of:
self.head.next.prev = None
self.tail = self.head.next
you want:
self.tail = self.tail.prev
self.tail.next = None

Related

Why does calling a function and writing statements explicitly work differently in Python?

I need an insert to head operation for a linked list that I implemented. However, doing this operation by function call (like insertToHead) and writing the statements explicitly where I need them produces different results. I wonder which property of Python leads to that difference but I couldn't figure it out.
To be more specific, let's say that I have the following class for the linked list:
class Node:
value = None
nextNode = None
def __init__(self, value):
self.value = value
def insertToHead(self, value):
newHead = Node(value)
newHead.nextNode = self
return newHead
For a linked list with a single element (say, 2) I want to insert a node (say, 0) to the head to make linked list 0 -> 2.
I created the linked list the following way
head = Node(2)
Then I tried to insert 0 to head two ways:
Writing the statements explicitly where I need them
newHead = Node(0)
newHead.next = head
head = newHead
Now head is 0, not 0 -> 2.
Calling insertToHead
head = head.insertToHead(0)
head is 0 -> 2 after this statement.
Does anyone know why these two approaches result in differently?
You have a typo. newHead.next should be newHead.nextNode.
A simple implementation of Singly Linked Lists:
class Node:
def __init__(self, value = None, nextNode = None):
self.value = value
self.nextNode = nextNode
class LinkedList:
def __init__(self):
self.head = None # will point to the head of the list
self.tail = None # will point to the tail of the list
self.size = 0 # size of the linked list
def insert_to_head(self, data):
# when push front, the head of the linked list will be Node()
self.head = Node(data, self.head)
if self.tail == None: # if tail is None, means it is a single element
self.tail = self.head
self.size += 1 # increase size by one
def __str__(self):
ret_str = ""
node = self.head
while node != None:
ret_str += str(node.value) + " -> "
node = node.nextNode
return ret_str
myLinkedList = LinkedList()
myLinkedList.insert_to_head(3)
myLinkedList.insert_to_head(2)
print(myLinkedList)

unable to access next node for Linked List while reversing a Linked List in python

I am a bit new to python and I have seen the correct solutions to the reversing the linkedlist problem but I wanted to know why my solution does not work. In particular, reverse function stays inside the while loop for the code below because of "new_list.head.next=prev" line
class Node:
def __init__(self, value):
self.value = value
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, value):
if self.head is None:
self.head = Node(value)
return
node = self.head
while node.next:
node = node.next
node.next = Node(value)
def __iter__(self):
node = self.head
while node:
yield node.value
node = node.next
def __repr__(self):
return str([v for v in self])
def reverse(linked_list):
new_list = LinkedList()
if linked_list is None:
return new_list
node = linked_list.head
new_list.head = node
while node.next:
prev = node
node = node.next
new_list.head = node
new_list.head.next = prev
return new_list
if __name__ == "__main__":
a = LinkedList()
b = [1,2,3,4,5]
for item in b:
a.append(item)
print a
c = reverse(a)
print c
If you tag your question with Python3 please make sure it runs in python 3.
The reason is because you are mixing up points and creating an infinite loop. Print the value and it may help you find the bug. I am going to use the values to point out the issue.
while node.next:
# First node that comes in value = 1
print(node.value) #
prev = node # prev = 1
node = node.next # node = 2
new_list.head = node # You are setting the head = 2 (i.e. head = node.next)
new_list.head.next = prev # You are setting next of head = 1 (i.e. head.next = node.next.next)
# however this also set node.next.next = 1
# So going into the next loop: node.value = 2 and node.next.value = 1
Because of this pointer confusion you are forever looping between your first and second node.
This is how your reverse can look:
def reverse(linked_list):
new_list = LinkedList()
if linked_list is None:
return new_list
node = linked_list.head
new_list.head = Node(node.value)
while node.next:
node = node.next
prev_head = new_list.head
new_list.head = Node(node.value)
new_list.head.next = prev_head
return new_list
With it I got desired output of print c: [5, 4, 3, 2, 1]
General advise: create new Node instead of assignment to node in initial list.
It's a little easier to reason about this (at least to me) if you think about two references:
• One to the remaining part of the original list you haven't seen
• One to the head of the new list
At each iteration move the remaining up and set the old remaining to the head of the new list. Order is important here — as you've seen it's easy to accidentally change next on two different variables that are pointing the same node if you're not careful:
def reverse(linked_list):
new_list = LinkedList()
if linked_list is None:
return new_list
remaining = linked_list.head
while remaining:
prev_head = new_list.head # the old head becomes the first link
new_list.head = remaining # new head becomese the first remaining
remaining = remaining.next # move remaing one up the chain
new_list.head.next = prev_head # point the new head to the previous
return new_list

Add a node in ascending order

def add(self, value):
#write your code here
if self.head == None:
new_node = Node(value)
self.head = new_node
self.tail = self.head
elif self.head.value > new_node.value:
new_node = Node(value)
new_node.value = value
new_node.next = self.head
self.head = new_node
else:
new_node = Node(value)
self.tail.setNext(new_node)
self.tail = new_node
self.count += 1
Above is the code for my function add. I am trying to add Node objects
into a linked list in ascending order so that when I print it it comes out as:
>>> x.add(8)
>>> x.add(7)
>>> x.add(3)
>>> x.add(-6)
>>> x.add(58)
>>> x.add(33)
>>> x.add(1)
>>> x.add(-88)
>>> print(x)
Head:Node(-88) Tail:Node(58)
List:-88 -6 1 3 7 8 33 58
But when I do it with my code above it prints it out as:
>>> print(x)
Head:Node(-88)
Tail:Node(1)
List:-88 -6 3 7 8 58 33 1
I'm 80% sure the problem is within the elif statement but I am not sure how to fix it to make go in ascending order.
The problem is in the last else.
So let's see what is going on:
number, action, result:
8 -> in the first if -> 8
7 -> in elif 8 > 7 -> 7,8
3,-6 -> same in elif -> -6, 3, 7, 8
58 -> last else -> -6, 3, 7, 8, 58
and here comes the error when 33 comes and enters the last else again since it is bigger than head which is -6, and the result becomes -6, 3, 7, 8, 58, 33.
To fix this you need to loop through the list to see where 33 needs to be placed, you can't have a sorted list just by placing the items at the start or at the end.
def add(self, value):
#write your code here
#if list is empty
if self.head == None:
new_node = Node(value)
self.head = new_node
self.tail = self.head
#elif value < head set new head
elif self.head.value > value:
new_node = Node(value)
new_node.value = value
new_node.next = self.head
self.head = new_node
#elif value > tail set new tail
elif value > self.tail.value:
new_node = Node(value)
self.tail.setNext(new_node)
self.tail = new_node
# and finally you need to loop to find the sweet spot
else:
# we will start the search from the head
current_node = self.head
# while the value we wish to insert is bigger than the next one
while value > current_node.next.value:
# set the current one to the next one
current_node = current_node.next
# finally we reached a node which is smaller than the value we wish to insert
# but its next node is bigger
new_node = Node(value)
# set the new nodes next to the bigger node
new_node.next = current_node.next
# and the curren't node's next to the new one
current_node.next = new_node

Singly linked list is not reversing when using recursion

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

Circular Linked list in python

def delete_node(head, value):
p=head
if p is None:
return None
while p.value!=value:
p=p.next
if p.next is head and p.value!=value:
return head
p.value=p.next.value
if p.next==head:
head=p
p.next=p.next.next
return head
The above is my code for deleting a node in a circular linked list based on value of the node!
The code doesn't gimme result for this case-- I've only 1 element in the list and i deleted it.. So the resultant should be an empty set.. But because i took p.value=p.next.value it points back again to itself and the same value is in the list! Can anyone help me out! Thanx in advance! :)
The easiest solution here is to have a dummy node that points to itself in case of an empty list. As a consequence in an empty list we have one node that points to itself (the dummy), in a list with one element the dummy points to the element and the element points to the dummy.
Avoids the need for any special cases and generally simplifies the code. To check if the list is empty you can then just do dummy.next is dummy, also nice.
enter link description here
class node:
def __init__(self, data):
self.data = data
self.next = None
class circularLinkedList:
def __init__(self):
self.head = None
self.tail = None
def insertNode(self, data):
newnode = node(data)
if self.tail is None:
self.head = self.tail = newnode
else:
newnode.next = self.head
self.tail.next = newnode
self.tail = self.tail.next
def printCLL(self):
head = self.head
tail = self.tail
while head.next is not None and head.next is not tail:
print(head.data, end="---")
head = head.next
print(head.data, end="---")
print(tail.data)
c = circularLinkedList()
c.insertNode(1)
c.insertNode(1)
c.insertNode(3)
c.insertNode(4)
c.insertNode(5)
c.printCLL()

Categories