Implementing an insert method on a linked list on python - python

Attempting to create a method for a singly linked list and struggling to understand why this test case is failing. In my second class SLinkedList, I have a method called insert, it takes the argument pos which is an integer. Now in my test case when I add middle to position 4 it stops referencing any further nodes the linked list meaning that the node containing the data middle does not have a reference to the node containing 77. I'm confused why this is happening? I've programmed it such that when current_pos==pos we set the next of our current (new_node) to be current.getNext() (77). Haven't I assigned the next of 2 to middle and the next of middle to 77?
class SLinkedListNode:
# an instance of this class is a node in a Single Linked List
# a node has a reference to data and reference to next
def __init__(self,initData,initNext):
self.data = initData
self.next = initNext
def getNext(self):
return self.next
def getData(self):
return self.data
def setData(self,newData):
self.data = newData
def setNext(self,newNext):
self.next = newNext
class SLinkedList:
# an instance of this class is a Singly-Linked List object
# it has reference to the first node in the list
def __init__(self):
self.head = None
self.size = 0
def add(self,item):
# adds an item at the start of the list
new_node = SLinkedListNode(item,None)
new_node.setNext(self.head)
self.head = new_node
self.size = self.size + 1
def append(self,item):
# adds an item at the end of the list
new_node = SLinkedListNode(item,None)
current = self.head # Start the traversal
if self.size == 0: # check if list is empty
self.add(item)
else:
while (current.getNext()!=None):
current= current.getNext() # traversing the list
current.setNext(new_node)
self.size = self.size +1
def insert(self,pos,item):
# inserts the item at pos
# pos should be a positive number (or zero) of type int
assert type(pos)==int,'Error:pos is not an integer'
assert pos>=0,'Error:pos must be positive'
current=self.head
new_node= SLinkedListNode(item,None)
if pos==0:
self.add(item)
elif pos==self.size:
self.append(item)
else:
current_pos=0
while(current.getNext()!=None):
if (pos-1)==current_pos:
print(current.getData())
current.setNext(new_node)
if pos==current_pos:
print(current.getData())
new_node.setNext(current.getNext())
current=current.getNext()
current_pos+=1
self.size+=1
# 1--> 2--->inserteditem---> 3-->4---> 5---> 6
# TO DO: write assert statement that tests if pos is int
# TO DO: write assert statement that tests that pos is not negative
# TO DO: COMPLETE THE METHOD
def remove(self,item):
# remove the node containing the item from the list
if self.size == 0:
raise Exception('List is Empty')
current = self.head
previous = None
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if not found:
raise Exception('Item not in list')
else:
if previous == None: # the item is in the first node of the list
self.head = current.getNext()
else: # item is not in the first node
previous.setNext(current.getNext())
self.size = self.size -1
def index(self,item):
# finds the location of the item in the list
if self.size == 0:
raise Exception('List is empty')
position = 0
found = False
current = self.head
while current != None and not found:
if current.getData() == item:
found = True
else:
current = current.getNext()
position = position + 1
if found:
return position
else:
return 'Item not found'
def pop(self):
# removes the node from the end of the list and returns the item
if self.size == 0:
raise Exception('List is empty')
current = self.head
previous = None
while current.getNext() != None:
previous = current
current = current.getNext()
if previous == None:
self.head = None
else:
previous.setNext(None)
self.size = self.size -1
return current.getData()
def __str__(self):
# returns a string representation of the list
current = self.head
string = ''
while current != None:
string = string + str(current.getData())+'->'
current = current.getNext()
return string
def getSize(self):
return self.size
def main():
# Testing Singly-Linked List
slist = SLinkedList()
slist.add(2)
slist.add(4)
slist.add('A')
slist.append(77)
slist.append(6)
slist.append('Z')
print('Original List:', slist.getSize(), 'elements')
print(slist)
print()
slist.insert(0,'start')
print('After inserting the word start at position 0:', slist.getSize(), 'elements')
print(slist)
print()
slist.insert(7,'end')
print('After inserting the word end at position 7:', slist.getSize(), 'elements')
print(slist)
print()
slist.insert(4,'middle')
print('After inserting middle at position 4:', slist.getSize(), 'elements')
print(slist)
if __name__=="__main__":
main()

Take a look at this code snippet from your insert-method:
else:
current_pos=0
while(current.getNext()!=None):
if (pos-1)==current_pos:
print(current.getData())
current.setNext(new_node)
if pos==current_pos:
new_node.setNext(current.getNext())
current=current.getNext()
current_pos+=1
Once the first if-condition is met, you're setting your new node as the current node's next node. Bear in mind, this new node has no reference to the rest of the list. The second if statement will not execute during this iteration, so the next line to be executed is current=current.getNext(), which sets current to be your new node, still without reference to the rest of the list. Therefore, in the next iteration of the while loop current.getNext() evaluates to None and your iteration terminates right then and there, effectively removing all nodes after your new node from the list.
To fix this, remove the second if and set the new node's next node in the previous if statement. This way, you'll maintain the reference to the rest of the list.
On a side note, get- and set-methods are very unpythonic, you can access and modify these attributes directly, e.g. by using current.next = whatever. Also, a while-loop iterating over your entire list seems fairly inefficient for the insert task since you exactly know the index to insert the new node into. Also also, your insert-method will break for positions greater than the list's length, you might want to add another check for that

Related

When a method has no return statement, how do I print out it's return value?

EDITED.
I am learning about Linked Lists. For each process applied by a Method, it is printed out to the console. So, adding, removing, searching (i.e, displaying the result of a search), are all streamed to stdout, but I cannot seem to do this for the insertion Method even though the insert Method is executed.
Some Methods have a return statement, while others rely on the __repr__() for conversion to string, to then be streamed to the console. The insertion Method (not mine, but a course worked example) takes two arguments and does not have a return statement. The most consistent error message I get when attempting to print is TypeError: %d format: a real number is required, not NoneType, or TypeError: not enough arguments for format string, where I have replaced %d with %s.
What I do not understand is, why I am unable to display test data for the insert Method, while I can do so for all others.
The code,
#!/usr/bin/env python3
class Node:
data = None
next_node = None
def __init__(self, data):
self.data = data
def __repr__(self):
return "<Node data: {}>".format(self.data)
# Linked List
class LinkedList:
def __init__(self):
self.head = None
def is_empty(self):
return self.head == None # corrected
def size(self):
current = self.head
count = 0
while current:
count += 1
current = current.next_node
return count
# Adding a node
def add(self, data):
new_node = Node(data)
new_node.next_node = self.head
self.head = new_node
# Searching the List
def search(self, key):
current = self.head
while current:
if current.data == key:
return current
else:
current = current.next_node
return None
# Inserting into the List
def insert(self, data, index):
if index == 0:
self.add(data)
if index > 0:
new_data = Node(data)
position = index
current = self.head
while position > 1:
current = current.next_node
position -= 1
past_node = current
future_node = current.next_node
past_node.next_node = new_data
new_data = current.next_node
# Removing a node from the List
def remove(self, key):
current = self.head
previous = None
found = False
while current and not found:
if current.data == key and current == self.head:
found = True
self.head = current.next_node
elif current.data == key:
found = True
previous.next_node = current.next_node
return current
def __repr__(self):
nodes = []
current = self.head
while current:
if current is self.head:
nodes.append("[Head: {}]".format(current.data))
elif current.next_node is None:
nodes.append("[Tail {}]".format(current.data))
else:
nodes.append("[{}]".format(current.data))
current = current.next_node
return '-> '.join(nodes)
Test output;
l = LinkedList()
l.add(1)
l.add(2)
l.add(3)
l.add(5)
l.add(6)
l.add(7)
length = l.size()
print("Size of list: {}".format(length)) # Size of list: 6
print(l) # [Head: 7]-> [6]-> [5]-> [3]-> [2]-> [Tail: 1]
seek = l.search(7)
print("Found: {}".format(seek)) # Found: <Node data: 7>
between = l.insert(4, 3)
if between is not None:
print(f"Inserted {between} at index {between}")
else:
print("A problem with code") # A problem with code
gone = l.remove(1)
print("Removed: {}".format(gone)) # Removed: <Node data: 1>
# Note the insertion of '4' at index 3
print(l) # [Head: 7]-> [6]-> [5]-> [4]-> [3]-> [Tail: 2]
THIS CODE WORKS!
Other variants of the print format have been tried f"{}", .format() and even an attempt at conversion to string str() was made, with no luck. Could someone explain exactly what the problem is (though, I believe it to be a NoneType issue) and how to resolve it?
I hope my question is clearer. Thank you.
There are several issues with the code you presented, including the following:
The Node class should not define data and next_node as class attributes. They should be instance attributes. Luckily, the constructor creates an instance attribute data (hiding the class attribute), but for next_node this is not done, which makes your linked list unusable.
In line with the previous comment, you should have self.next_node = None in your constructor.
[You corrected this in an edit to your question: The method name is_empty suggests that it will return a boolean indicating whether the list is empty or not. But instead it makes the list empty. That seems wrong.]
[You corrected this in an edit to your question: insert can call a method add which is not defined.]
In insert, when index is 0, the code will still continue after the first if and reference a variable new_data that has not been defined (since the second if condition was not true). You should avoid that any of the other code is executed when index is 0. You can do this with a return.
In insert, in the while loop there is no verification whether current is None. If that happens, current = current.next_node will raise an error.
new_data = current.next_node is useless and leaves the next_node attribute of next_node uninitialised.
Not an issue, but in remove, the found name is not very useful. Just break out of the loop when the node has been found and removed. Also, avoid having the current.data == key condition executed twice for the same node.
In remove, in the loop, you never change current nor previous, and so the loop hangs.
[You corrected this in an edit to your question: In the main code, the list is empty at the moment that l.insert is called, so it is strange to pass 3 as value for the index parameter, as that index is out of range. As mentioned in a previous bullet, this will trigger an error. If you want to add a node at index 3, you'll first have to add nodes at indexes 0, 1, and 2.]
The insert method does not return anything, so capturing its return value is not going to give you anything else than None. If you really want to get some feedback from it, then do like you did for the remove method: have it return the relevant node. In that case you should also let add have a return value.
Here is some working code with the above issues addressed and more:
class Node:
def __init__(self, data):
self.data = data
self.next_node = None # next_node neads to be an instance attribute, not a class attribute
def __repr__(self):
return "<Node data: {}>".format(self.data)
class LinkedList:
def __init__(self):
self.head = None
def is_empty(self):
return self.head == None # Don't MAKE it empty!
def size(self):
current = self.head
count = 0
while current:
count += 1
current = current.next_node
return count
def add(self, data):
new_node = Node(data)
new_node.next_node = self.head
self.head = new_node
return new_node # Return the new node
def search(self, key):
current = self.head
while current:
if current.data == key:
return current
else:
current = current.next_node
return None
def insert(self, data, index):
if index == 0:
# Don't continue after this call to self.add
return self.add(data) # Return the new node
current = self.head
while index > 1 and current: # Protect against out of range index
current = current.next_node
index -= 1
if current: # Protect against out of range index
new_data = Node(data)
# Make sure the new node gets a next_node assignment
new_data.next_node = current.next_node
current.next_node = new_data
return new_data # Return the new node
def remove(self, key):
current = self.head
previous = None
while current:
if current.data == key: # Check this only once per node
if current == self.head:
self.head = current.next_node
else:
previous.next_node = current.next_node
break # No need for variable - just exit
previous = current # Update previous
current = current.next_node # Move to next node
return current
def __repr__(self):
nodes = []
current = self.head
while current:
if current is self.head:
nodes.append("[Head: {}]".format(current.data))
elif current.next_node is None:
nodes.append("[Tail {}]".format(current.data))
else:
nodes.append("[{}]".format(current.data))
current = current.next_node
return '-> '.join(nodes)
l = LinkedList()
l.add(1)
l.add(2)
l.add(3)
l.add(5)
l.add(6)
l.add(7)
length = l.size()
print("Size of list: {}".format(length)) # Size of list: 6
print(l) # [Head: 7]-> [6]-> [5]-> [3]-> [2]-> [Tail: 1]
seek = l.search(7)
print("Found: {}".format(seek)) # Found: <Node data: 7>
node = l.insert(4, 3)
print("Inserted {}".format(node)) # Inserted: <Node data: 4>
gone = l.remove(1)
print("Removed: {}".format(gone)) # Removed: <Node data: 1>
# Note the insertion of '4' at index 3
print(l) # [Head: 7]-> [6]-> [5]-> [4]-> [3]-> [Tail: 2]
insert does not "have a problem" outputting data - just like standard Python lists, it is an in-place operation. You are modifying the list on which it is applied.
insert() does not need to return anything, as all the information you need is provided by you when calling it - you need to pass a list, you need to pass data to insert and you need to pass an index at which the element is to be placed - there is no new information to be gained from returning anything.
Related question:
Why don't list operations return the resulting list?
I think you're confusing 2 things here. The value after the return statement is what the function call is replaced with when the function is called. So for example:
def square(x):
return x*x
square(4)
here the square(4) would be replaced with 4*4. And if you don't explicitly use a return statement than a None is returned after the last command in the function/method.
Whereas repr() is a way to specifiy the string representation of that object. So for example:
class A:
pass
a = A()
print(a)
might create a cryptic output of <main.A at 0x7fbc841c9490>. So if you want it to be more descriptive you could add a repr() method:
class Point:
def __init__(self, x,y):
self.x = x
self.y = y
def __repr__(self):
return f"Coordinates of the point are x: {self.x}, y: {self.y}"
p = Point(2,4)
And instead of the cryptic default message you'd get:
Coordinates of the point are x: 2, y: 4
So the representation is how the obj is converted to a string whereas the return value is what the function call is replaced with.
print is TypeError: %d format: a real number is required, not
NoneType, or TypeError: not enough arguments for format string, where
I have replaced %d with %s.
So this creates errors because %d and %s expect numbers and strings when the return type of a method without return is None.

How do I call a method from my outer class in an inner class of my custom-made linked list?

I'm learning python and challenging myself by writing my own linked list from scratch. I'm using a tradition structure of an inner node class which holds a piece of data and a reference to the next node. Right now I'm trying to create a __repr__ method that returns a string representation of a node. The string it returns looks like this: "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
It works fine unless there's only 1 node in the list, which gave me the following error: AttributeError: 'NoneType' object has no attribute 'data'.
To get around this, I check first to see if there's only one node in the list, and I return the following string: "This node contains {0}. There is no next node." .format(self.data)
This is what my __repr__ method looks like right now:
def __repr__(self):
if MyLinkedList.get_size() == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
This is what the whole linked list class looks like so far:
class MyLinkedList(object):
head = None
size = None
def __init__(self):
self.size = 0
def get_head(self):
return self.head
def get_size(self):
return self.size
def is_empty(self):
if self.size == 0:
return True
else:
return False
def __repr__(self):
result = "["
curr = self.head
while curr != None:
if curr.next == None:
result += curr.data
break
result += curr.data
result += ", "
curr = curr.next
result += "]"
return result
def add_to_head(self, data):
new_node = MyLinkedList.Node(data)
if self.size == 0:
self.head = new_node
else:
new_node.next = self.head
self.head = new_node
self.size += 1
def delete_from_head(self):
if (self.size == 0):
self.head = None
else:
new_head = self.head.next
self.head = new_head
self.size =- 1
class Node():
next = None
def __init__(self, data):
self.data = data
def get_data(self):
return self.data
def get_next(self):
return self.next
def __repr__(self):
if MyLinkedList.get_size() == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
But now when I try to print the string representation of any node, it gives me the following error: TypeError: get_size() missing 1 required positional argument: 'self'
Is there any way to fix this issue? All I'm trying to do is to call my outer class's get_size() method in my inner node class, and check if that value is 1. Is there any other way to make it so my node's __repr__ method returns the string I want it to return when there's only one node in the list?
Also, if you spot other improvements you could make to my code, I would be glad to hear them.
You can only invoke get_size() on an instance of your MyLinkedList class. A node shouldn't know anything about the linked list class anyway. Just take advantage of the node's next pointer instead:
def __repr__(self):
suffix = "There is not next node" if self.next is None else "The next node is {}".format(self.next.data)
return "This node contains {}. {}.".format(self.data, suffix)
I believe you need to add a self argument into the get_size() method. Such as:
def __repr__(self):
if MyLinkedList.get_size(self) == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)

How do I remove node in Linked List?

I am implementing Linked List by using Python. Here is my code
class Node:
def __init__(self, value):
self.value = value
self.next = None
class LinkedList:
def __init__(self, head = None):
self.head = head
def append(self, newElement):
current = self.head
if self.head:
while current.next:
current = current.next
current.next = newElement
else:
self.head = newElement
#get position time complexity O(n)
#get the node at a specific position
def get_position(self, position):
current = self.head
current_pos = 1
while current_pos <= position:
if current_pos == position:
return current
current = current.next
current_pos += 1
return None
#Insert element
# Time complexity O(n)
def insert_element(self, element, position):
if position > 1:
front_pos = self.get_position(position - 1)
end_pos = self.get_position(position + 1)
front_pos.next = element
element.next = end_pos
else:
element.next = self.head
self.head = element
def delete_element(self, element):
current = self.head
previous = None
while current:
if current.value == element:
if not previous:
self.head = element
previous.next = current.next
else:
current = current.next
previous = current
else:
return False
node1 = Node("Iron Man")
node2 = Node("Capitain America")
node3 = Node("Doctor Strange")
node4 = Node("Spider man")
node5 = Node("Rieder")
print("Node1 Value is {}".format(node1.value))
print("Node1 next Value is {}".format(node1.next))
print("Node2 Value is {}".format(node2.value))
print("Node2 next Value is {}".format(node2.next))
Avengers = LinkedList()
Avengers.append(node1)
print("Firt element in link list is {}".format(Avengers.head.value))
Avengers.append(node2)
print("After Iron Man is {}".format(Avengers.head.next.value))
print(Avengers.get_position(2).value)
Avengers.append(node3)
Avengers.append(node4)
Avengers.insert_element(node5, 4)
print(Avengers.get_position(4).value)
Avengers.delete_element(node5)
print(Avengers.get_position(4).value)
Here is my output:
Node1 Value is Iron Man
Node1 next Value is None
Node2 Value is Capitain America
Node2 next Value is None
Firt element in link list is Iron Man
After Iron Man is Capitain America
Capitain America
Rieder
Rieder
The list structure link like this: Ironman -> Capitain America -> Doctor Strange -> Rieder -> Spiderman
Therefore, if I do not want the "Rieder" node. The last line of output should show "Spider man"
What happened in my code? Really appreciate for people who help me out :D
One issue is that you appear to update current and then update previous with the new value of current. I would go back to your delete method, and go through it line by line. It helps if you write out on paper what the state of your list is at each line, and see if you can correct the algorithm.
else:
current = current.next
previous = current
First you need to fix your insert_element function. You are inserting nodes incorrectly. This is the correct way to do it
def insert_element(self, element, position):
if position > 1:
front_pos = self.get_position(position - 1)
# end_pos should not be position + 1, because it will
# skip the element at index=position
end_pos = self.get_position(position)
front_pos.next = element
element.next = end_pos
else:
element.next = self.head
self.head = element
Next, you need to only pass the value of the nodes and not the nodes themselves while deleting the elements. I have made some minor changes to your code to fix some bugs. See the comments for more information
def delete_element(self, element):
current = self.head
previous = None
while current:
if current.value == element:
# This means that the head itself
# has to be deleted
if not previous:
self.head = self.head.next
# Add an else block correctly here
# in case head is not to be deleted
else:
previous.next = current.next
# break the loop when the element is deleted
break
else:
# First copy the current into previous,
# Then change the value of current
previous = current
current = current.next
Also there is no need for else: return False block at the end of delete function.

Unexpected output when popping last element of linked list

I have made a Node and Deque class to represent a double linked list. I wrote a function to pop the last item of my list, but after executing the function it pops the first element instead.
My expected output:
my_list.push_front(1)
my_list.push_front(2)
my_list.push_front(3)
linked list is [3, 2, 1]
my_list.pop_back() --> [3,2]
My linked list function file:
class Node:
"""
Initialize empty node
"""
def __init__(self, data, prev = None, next = None):
self.data = data
self.next = next
self.prev = prev
class Deque:
"""
A double-ended queue
"""
def __init__(self):
"""
Initializes an empty Deque
"""
self.head = None
self.size = 1
def __len__(self):
"""
Computes the number of elements in the Deque
:return: The size of the Deque
"""
counter = 1
current = self.head
if self.head is None:
return 0
while current.next is not None:
counter += 1
current = current.next
return counter
def push_front(self, e): #needs work
"""
Inserts an element at the front of the Deque
:param e: An element to insert
"""
new_head = Node(data = e, next = self.head)
if self.head:
self.head.prev = new_head
self.head = new_head
def pop_back(self):
"""
Removes and returns the last element
:return: The (former) last element
"""
if self.head == None:
raise IndexError
curr = self.head
while curr.next:
curr = curr.next
save = self.head.data
self.head = self.head.next
self.size -= 1
return save
def listprint(self, node):
"""
Prints each element of the node front to back
:param node:
"""
while (node is not None):
print(node.data)
last = node
node = node.next
My main file:
def main():
my_list = Deque()
my_list.push_front(1)
my_list.push_front(2)
my_list.push_front(3)
my_list.pop_back()
my_list.listprint(my_list.head) #print elements in the list
In my pop_back() function, I was thinking of traversing until the end of the linked list, and then set self.head to self.head.next and decrease the size of the linked list by 1.
Here's what I think is the issue:
save = self.head.data
self.head = self.head.next
You want to remove the last one, but you're actually changing the reference for the head. If you want to change the reference of the last one, you should be doing:
while curr.next.next: # This allows you to stand on the previous of the last one
curr = curr.next
save = curr.next
curr.next = None
self.size -= 1
return save
The actual thing you're doing there it's a pop and not a dequeue

Algorithm to delete a node from the middle

I'm reading Cracking the Coding Interview and doing practice problems and I'm stuck on this one:
"Implement an algorithm to delete a node in the middle (i.e., any node but the first and the last node, not necessarily the exact middle) or a singly linked list, given only access to that node.
EXAMPLE
Input: the node from the linked list a->b->c->d->e->f
Result: nothing is returned, but the new linked list looks like a->b->d->e->f"
Here's my code :
class Node:
def __init__(self, data = None, nextnode = None):
self.data = data
self.nextnode = nextnode
def __str__(self):
return str(self.data)
class LinkedList():
def __init__(self, head = None):
self.head = head
def insert(self, data):
new_node = Node(data)
new_node.nextnode = self.head
self.head = new_node
def remove(self, data):
current = self.head
absent = True
if current == None: print('List is empty')
if current.data == data:
self.head = current.nextnode
absent = False
while current.nextnode:
if current.nextnode.data == data:
absent = False
if current.nextnode.nextnode:
current.nextnode = current.nextnode.nextnode
else: current.nextnode = None
else: current = current.nextnode
if absent: print('Element not in list')
def size(self):
current = self.head
size = 0
while current:
current = current.nextnode
size += 1
return size
def find(self, data):
current = self.head
if current == None: print('List is empty')
search = True
while current and search:
if current.data == data:
print(current)
search = False
current = current.nextnode
if search: print('Not found')
def print_list(self):
current = self.head
while current:
print(current, end = ' ')
current = current.nextnode
print('')
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node1.nextnode = node2
node2.nextnode = node3
node3.nextnode = node4
list1 = LinkedList(node1)
list1.insert(2 ****EDITED node2 to 2 here****)
print_list(list1)
def delmid(ll, n):
current = ll.head
if current == n:
print('Can\'t delete first node')
return
while current.nextnode:
if current.nextnode == n:
if current.nextnode.nextnode:
current.nextnode = current.nextnode.nextnode
return
else:
print('Can\'t delete last node')
return
delmid(list1, node2)
print_list(list1)
I can't figure out why it doesn't seem to think that ll.head and node2 are the same ... It does work if I get rid of the line list1.insert(node2) ...
I don't understand ...
EDIT: after reading the first sentence of the solution in the book, apparently i did it wrong anyways .... "given only access to that node" means you don't know the head of the list ... back to the drawing board ...
Because your insert method is wrong:
def insert(self, data):
new_node = Node(data)
new_node.nextnode = self.head
self.head = new_node
Your method does not insert node2 itself as a node: it creates a new node with node2 as payload (data). That is something different.
You can define a method:
def insert_node(self, node):
node.nextnode = self.head
self.head = new_node
Nevertheless this will create a loop since now node1 will be pointing to node2 and node2tonode1`. So the resulting linked list will be a rounded list with two elements, like:
node1 --> node2
^---------/
EDIT: since you solved that one. There is also a problem with your delmid method.
The main problem is that in your while loop you need to walk through the linked list, and you do not do that: current always remains the same, so:
def delmid(ll, n):
current = ll.head
if current == n:
print('Can\'t delete first node')
return
while current.nextnode:
if current.nextnode == n:
if current.nextnode.nextnode:
current.nextnode = current.nextnode.nextnode
return
else:
print('Can\'t delete last node')
return
current = current.nextnode
Should fix that.
Insert operation
You misunderstood your own insert-implementation.
list1.insert(node2) inserts a new node with node2 as content:
def insert(self, data):
new_node = Node(data) # <== wrap node2 into another node instance
new_node.nextnode = self.head
self.head = new_node
Comparison of Nodes
The ==-operator internally works by calling the method __eq__(self, other). In your case you didn't provide and implementation for this method, so the default is used to compare by all variables, which includes nextnode. Thus two nodes can only be equal, if they are precisely the same. To get this corrected, use a custom comparison method in Node:
def __eq__(self, other):
return type(other) is Node && other.data == self.data
This __eq__-method would work by first checking that other definitely is of type Node and afterwards compare by the data stored in each instance.
Delmid
Going a bit further than the actual question:
while current.nextnode:
if current.nextnode == n:
if current.nextnode.nextnode:
current.nextnode = current.nextnode.nextnode
return
else:
print('Can\'t delete last node')
return
This loop will run infinitely, unless the list has at most a size of 1. To fix this step through the list by current = current.nextnode.
Improving Delmid
The actual purpose of this task was to get you used to another way of manipulating linked lists: swapping.
Instead of searching the entire list for the predecessor of n, you could just check that n is neither the first nor the last node, swap out the value with the value of it's consecutive node and delete the consecutive node:
def delmid(ll, n):
if ll.head == n:
print('Can\'t delete first node')
return
if n.nextnode is None:
print('Can\'t delete last node')
return
# swap values
tmp = n.data
n.data = n.nextnode.data
n.nextnode.data = tmp
# remove node
n.nextnode = n.nextnode.nextnode

Categories