How to call a function and add it to another function. python - python

I have three functions that I have been working on. The first function removes the head of a linked list. The second removes the tail from the linked list. The third I need to called remove tail, remove head function, and add it to remove function. The third function goal is to remove values in the middle. The code right now prints out all of the numbers instead of slowly taking the tail off.
I would like to call this function within remove function.
def remove_tail(self):
if self.head is None:
return None
removed_tail = self.tail
if removed_tail == self.head:
self.head = None
self.tail = None
else:
current = self.head
while current.next != self.tail:
current = current.next
removed_tail = current
current.next = None
self. Tail = current
def remove(self, value, remove_tail):
curr = self.head
while curr is not None:
if curr.data == value:
# If the location of 'value' is at the end of the list,
# then we can call insert_tail to add 'new_value'
if curr == self.tail:
self.insert_tail(value)
else:
new_node = LinkedList.Node(value)
new_node.prev = curr
new_node.next = curr.next
curr.next.prev = new_node
curr.next = new_node
return # We can exit the function after we insert
curr = curr.next # Go to the next node to search for 'value'
I was trying to see if I could take self. Tail and add remove tail function to it. If current is equal to self. Tail it seems to me I can added remove tail to it?
if curr == self.tail:
self.insert_tail(value)

Related

Can't add node in circular linked list if nodes are made without the function

I need to add a node at the start of the circular linked list
I can add the node if the list is empty and I add every node using the function but if the list already has nodes then the function doesn't work
class Node:
def __init__(self,data):
self.data = data
self.ref = None
class CircularLinkedList:
def __init__(self):
self.head = None
self.tail = None
def atStart(self,newData):
newNode = Node(newData)
if self.head is None:
self.head = newNode
self.tail = newNode
self.tail.ref = newNode
if self.tail:
newNode.ref = self.tail.ref
self.tail.ref = newNode
def printList(self):
if self.head is None:
print("Circular linked list is empty")
n = self.head
while n:
print(n.data)
n = n.ref
if n == self.head:
break
cll = CircularLinkedList()
cll.head = Node("Monday")
e2 = Node("Tuesday")
e3 = Node("Wednesday")
cll.head.ref = e2
e2.ref = e3
e3.ref = cll.head
cll.atStart("Sunday")
cll.atStart("Monday")
cll.printList()
I need to change the head node using the atStart function and print the whole list but it prints the three nodes I have made (cll.head,e2,e3) or just the newNode using the atStart if list is empty
I tried debugging and seems like whenever the list is not empty it only takes the newNode data and doesnt set the ref of it head node and just prints the old three nodes
There are a few issues:
In atStart the if self.tail: condition will always be true. This is problematic, as only one of the two alternatives should be executed, not both. Yet when the first block executes (the case where the list was empty), self.tail gets a value, so this second if block will also execute (the case where the list is not empty). This should not be an if, but just a plain else: -- you want to cover all remaining cases (where the list was not empty).
In the case the list was not empty, the atStart method does not make the new node the head of the list, so the list becomes inconsistent.
The main program should not tinker with the head or ref attributes -- this should be left to the class code only. This leads to all kinds of problems. In this case the problem is that cll.tail remains None, and so the list is inconsistent and you cannot rely on the correct execution of the atStart method, because that assumes that tail was set correctly. You could add cll.tail = e3 to fix that particular issue, but again, this is bad practice. The logic for attaching nodes to eachother should be performed within the class code only.
Not a problem, but it is overkill for a circular list to have two node references (head and tail): in a non-empty list the head always follows the tail node, so there really is no need to remember what the head is when you have the tail. In short: just keep track of the tail.
Here is the adapted code:
class Node:
def __init__(self,data):
self.data = data
self.ref = None
class CircularLinkedList:
def __init__(self):
# No need for a head reference, as it is always coming after the tail
self.tail = None
def atStart(self,newData):
newNode = Node(newData)
if self.tail is None:
self.tail = newNode
else: # Must be ELSE to avoid executing both blocks
newNode.ref = self.tail.ref
self.tail.ref = newNode # new node always becomes the head
def printList(self):
if self.tail is None:
print("Circular linked list is empty")
n = self.tail.ref
while n:
print(n.data)
if n == self.tail:
break
n = n.ref
The main program corrected:
cll = CircularLinkedList()
# Don't alter the atttributes of the linked list or its nodes! Only use methods:
cll.atStart("Wednesday")
cll.atStart("Tuesday")
cll.atStart("Monday")
cll.printList()
print("----")
cll.atStart("Sunday")
cll.atStart("Monday")
cll.printList()
From www.geeksforgeeks.org:
The circular linked list is a linked list where all nodes are
connected to form a circle. In a circular linked list, the first node
and the last node are connected to each other which forms a circle.
There is no NULL at the end.
def atStart(self,newData):
if self.head is None:
# Initialize head and tail
newNode = Node(newData)
self.head = newNode
self.tail = newNode
else:
# Copy the old head node
old_head = self.head
# Make a new head node
self.head = Node(newData)
# Update the reference of the new head node to point the old_head
self.head.ref = old_head
# Now update the tail ref to point to the head (circular)
self.tail.ref = self.head
Explanation:
In this code if head and tail are empty, for the the first call of atStart method the head and tail will be the same, that's self.head is self.tail which evaluates to True. It's possible to implement this code a better way, where for example __init__ or atStart methods should except two values instead of one to initialize the head and tail together. Because in this way, the data of the head will reside in the tail uselessly even after adding a new head. If you call this:
cll.head.data == cll.tail.data
Now it's interesting what you will get. The first call to atStart method will evaluate to True, the second call may not evaluate to true. That's, whatever the first cll.head.data held will be there in cll.tail.data always. Effectively we're wasting cll.tail.data.
A better approach would be
def atStart(self, *newData):
if self.head is None:
# Initialize head and tail (each with its own data)
self.head = Node(newData[0])
self.tail = Node(newData[1])
# tail references head (circular)
self.tail.ref = self.head
#rest of code from above....
The second call to the atStart method will update the head of the circular list to point to the new head node (created to add the last value) and the tail reference will be updated to point back to the new head. Note that the ref attribute of the new head will be updated to reference the old head, it's a very important step or you will end up losing your old data.

Merge Two Sorted Lists Python

I'm trying to solve merge sorted linked list problem.For that i've created three method. addlast , print and merge For linked list class created a three objects obj and obj1 (To create two linked list) Using obj3 calling a merge method by passing both linked list head pointer.But here it only print linked list 1 instead of both.
My expected result should be creating both linked list and print the sorted linked list.So what could be the reason the my code isn't working ?
class node:
def __init__(self,data) :
self.data = data
self.next = None
class linkedlist:
def __init__(self) :
self.head = None
def addlast(self,data):
newnode = node(data)
if self.head == None:
self.head = newnode
else:
current = self.head
while(current.next != None):
current = current.next
current.next = newnode
def print(self):
current = self.head
while(current):
print(current.data, "-->",end="")
current = current.next
print("NUll")
def merge(self,obj,obj1):
current = obj.head
current2 = obj1.head
newnode = node(None)
while current and current2 != None:
if (current == None):
newnode.next = current2
break
if (current2 == None):
newnode.next = current
break
if current.data <= current2.data:
newnode.next = current
current = current.next
print(newnode.data)
newnode = newnode.next
else:
newnode.next = current2
current2 = current2.next
print(newnode.data)
newnode = newnode.next
if current:
newnode.next = current
if current2:
newnode.next = current2
print(newnode.data)
obj = linkedlist()
obj.addlast(10)
obj.addlast(20)
obj.addlast(30)
obj.addlast(40)
obj1 = linkedlist()
obj1.addlast(50)
obj1.addlast(60)
obj1.addlast(70)
obj1.addlast(80)
obj3 = linkedlist()
obj3.merge(obj,obj1)
There are several issues in your code:
You created merge as an instance method, but never use self. If the intention is to merge a second list into the current list (self), then you need only one argument, i.e. the "other" list, not two.
merge should set the head attribute of the list that gets the merge result, i.e. self.head should be assigned a reference.
newnode progresses through the merged list, but your code loses track of what was the first node of that merged list, so you need an extra name so that you can later assign to self.head
if (current == None) is never going to be a true condition, as the while condition requires that both current and current2 are not None. The same goes for if (current2 == None): it will never be true.
The code to link the only remaining list (the last two if statements) should be put after the loop, not inside it. Only after the loop it is certain that one of current or current2 is None. Also that code can be simplified with the use of the or operator.
The reason you only see part of the merged list printed, is that you don't print the nodes that are appended ("in bulk") in that last operation.
Here is the corrected code:
def merge(self, obj1): # Only need one argument, not two
current = self.head # Use self
current2 = obj1.head
dummy = newnode = node(None) # keep track of this dummy node
while current and current2:
if current.data <= current2.data:
newnode.next = current
current = current.next
else:
newnode.next = current2
current2 = current2.next
newnode = newnode.next # This is common to both if/else case
newnode.next = current or current2 # out of the loop
self.head = dummy.next # update current list's head reference
The calling code should have near the end:
obj.merge(obj1)
obj.print()
This links the 2 lists Studrec and Aves then sort Studrec pulling the corresponding items from Ave.
def revert(Studrec, Ave):
Studrec, Ave = map(list, zip(*sorted(zip(Studrec, Ave), key=lambda x: x[0])))
return Studrec, Ave
Studrec, Ave = revert (Studrec, Ave)
In the merge function you're not updating the self.head, instance variable your code is merging the list but you're not keeping track of the head. Modify your code merge function to point the self.head to new node:
def merge(self,obj,obj1):
current = obj.head
current2 = obj1.head
newnode = node(None)
self.head=newnode

Remove the last node from a circular linked list in python

I am trying to pop elements from the end of a circular linked list in python. All the elements are getting popped successfully, but error arises when only one node is left. When the last remaining node is popped an error occurs.
Is there any problem with the show function?
class Node(object):
def __init__(self, value):
self.value = value
self.next = None
class CircularLinkedList(object):
def __init__(self, tail = None):
self.tail = tail
def append(self, value):
new_node = Node(value)
if self.tail ==None:
self.tail = new_node
self.tail.next = new_node
if self.tail!=None:
new_node.next= self.tail.next
self.tail.next=new_node
self.tail= new_node
#pop function
def pop(self):
if self.tail==None:
print("Sorry no elements .")
else:
if self.tail.next==self.tail:
self.tail=None
return self.tail
else:
temp=self.tail.next
while temp.next!=self.tail:
temp=temp.next
temp.next=self.tail.next
removed = self.tail
self.tail=temp
print("Popped")
return removed
#display function
def show(self):
current = self.tail
while current.next:
current = current.next
print(current.value)
if current == self.tail:
break
circular = CircularLinkedList()
circular.append(5)
circular.append(9)
circular.append(96)
print("Before deleting:")
circular.show()
circular.pop()
circular.pop()
circular.pop()
circular.show()
Image of the output
When you pop the last element you assign None to self.tail, so in show() in
while current.next:
you are trying to call next from None. Add a check
while current and current.next:
I fixed it so your image shows. The problem is not the popping of the last value. You're doing that correctly. The problem is that your show function cannot handle an empty list. You need to check for that.
You need to make a little change to you pop function to understand how many elements got popped.
Like this
def pop(self):
if self.tail==None:
print("Sorry no elements .")
else:
if self.tail.next==self.tail:
self.tail=None
print("popped") #HERE
return self.tail
else:
temp=self.tail.next
while temp.next!=self.tail:
temp=temp.next
temp.next=self.tail.next
removed = self.tail
self.tail=temp
print("Popped")
return removed
This will make things clear for you. And as others said, the reason for your error is in your show method.
You're making self.tail=None for the last element in pop(). And you're trying to access .next for None.
Here:
def show(self):
current = self.tail
while current.next:
Add an if like this to avoid error
def show(self):
current = self.tail
if current.next == None:
print("circular linkedlist is empty")
return None
while current.next:
current = current.next
print(current.value)
if current == self.tail:
break
There are the following issues in your code:
In the pop method None is returned when the only remaining node is removed. But it should return the removed node. This happens here:
self.tail=None
return self.tail # <-- this will always be None!
Change the pop function to this:
def pop(self):
if not self.tail:
print("Sorry no elements.")
else:
removed = self.tail # also when the only remaining node!
if self.tail.next == self.tail:
self.tail = None
else:
temp = self.tail.next
while temp.next != self.tail:
temp = temp.next
temp.next = self.tail.next
self.tail = temp
print("Popped")
return removed
The show method assumes that the list is not empty. For an empty list it will generate an error on this statement:
while current.next:
So deal with the empty list case separately. Also, it is strange to have this while condition, as there is a break inside the loop, that depends on a more useful condition: make that the loop condition. You could write that method as follows:
def show(self):
if not self.tail:
print("(empty)")
return
current = self.tail.next
while current != self.tail:
print(current.value, end=" ")
current = current.next
print(self.tail.value)
Not an error, but the append method is a bit weird in how it inserts the first node: it will execute both the if blocks. It ends up working correctly, but I find that counter-intuitive. It seems better to write this as an if...else structure, so that exactly one block of code gets executed, not two:
def append(self, value):
new_node = Node(value)
if self.tail:
new_node.next = new_node
else:
new_node.next = self.tail.next
self.tail.next = new_node
self.tail = new_node

singly linked list infinite loop when inserting an element at the end python

When I want to add an element at the end of my linked list it result in an infinite loop, here's the method i used
#insert at the end of the list
def append(self, data=None):
new_node = Node(data)
if self.head is None:
self.head = new_node
itr = self.head
while itr.next:
itr.next = new_node
itr = itr.next
Your code should return after adding the new node at the head if the list is empty.
After that your while loop is an infinite loop
You need to update it's position BEFORE you add to it and then add to it outside the loop
def append(self, data=None):
new_node = Node(data)
if self.head is None:
self.head = new_node
return
itr = self.head
while itr.next:
itr = itr.next
itr.next = new_node
Also your Node() should have a parameter for next
Your problem is in the loop:
while itr.next:
itr.next = new_node
itr = itr.next
When appending an item to an empty list, itr is initially is equal to new_node.
You set itr.next to new_node, so now new_node.next is equal to new_node. You have now created a cycle in your list, so it will loop forever.
When appending an item to your list, you should only be modifying the last element - the loop only serves to traverse the list to get to the end. It should look like:
def append(self, data=None):
new_node = Node(data)
if self.head is None:
self.head = new_node
else:
# you only need to traverse the list when it isn't empty
itr = self.head
while itr.next:
itr = itr.next
itr.next = new_node # only add the new node after traversing the list
That being said, this method of appending is O(n) complexity, and you can make it O(1) by keeping a pointer to the tail of the list and modifying the tail without traversing the list to find it.

How do I update my self.tail in a method for deleting a node at a specific position when the position is the last node?

Method obtained from googling and changed a bit:
def removeNodeAtPosition(self, position):
if self.head == None:
print("List is empty")
return
current = self.head
if position == 0:
self.head = current.next
current = None
self.size-=1
return
for i in range(position-1):
current = current.next
if current == None:
break
if current == None:
return
if current.next == None:
return
next = current.next.next
current.next = None
current.next = next
if position == self.size: #two lines here used for updating self.tail
self.tail = current
self.size-=1
The title mostly says it: my self.tail value isn't changed when I delete the tail of my node with this method. I don't know if I've gone wrong with the placement of my two lines of code or if the code is wrong. I've left the most recent attempt intact. Any help resolving this will be appreciated.
Think of removing current.next instead of removing current. This way if current.next is the last element, then current would be the new tail

Categories