Sorting a linked list (swapping nodes) - python

I am trying to sort a LinkedList containing of such nodes:
class Node:
def __init__(self, data, next=None):
self.data, self.next = data, next
The idea is to get a sequence (list/tuple/string) from which a LinkedList is created:
def __init__(self, seq):
self.front = self.Node(None)
node = self.front
for item in seq:
node.next = self.Node(item)
node = node.next
The logic of the sort is that in each iteration you go through the linkedlist, compare each item with the first, if its smaller/greater then you perform a swap, continue going through the linked list and do the same comparation and operation. Shown here:
def min_sort(self, reverse):
self.count = 0
first = self.front
while first.next is not None:
print(self.get_list())
self.progress(first, reverse)
self.count += 1
first = first.next
def progress(self, first, reverse):
node = first
if (node is None): return
while True:
node = node.next
if node is None or node.next is None: break
if (reverse):
if (first.next.data < node.next.data):
self.vymen(first, node, node.next)
else:
if (first.next.data > node.next.data):
self.vymen(first, node, node.next)
The issue is with the swap itself (function self.vymen(...)), where with basic LinkedList of numbers it usually works, however when loaded with a string e.g. "I code in Python", the spaces dont get swapped to the correct position making the whole result messed up.
The swap itself is:
def vymen(self, first, prev, swap):
prevX, currX, prevY, currY = first, first.next, prev, swap
if (self.front.next.data == swap.data): return
if (currX is None or currY is None): return
if (currX != prevY):
if (prevX is not None): prevX.next = currY
else: self.front = currY
if (prevY is not None): prevY.next = currX
else: self.front = currX
temp = currX.next
currX.next = currY.next
currY.next = temp
else:
temp = currY.next
currX.next = temp
currY.next = currX
prevX.next = currY
Its really just an issue with the spaces as I can see and I dont understand whats the issue, since it swaps the first space on the beginning but not the rest. I would be thankful if someone would be able to point out a mistake or give me an idea whats happening :)

There are two issues in your code:
The following statement in vymen is the cause of trouble:
if (self.front.next.data == swap.data): return
Its intention is to avoid an unnecessary swap when the data of the swapped nodes are equal. But then it should not reference self.front.next, but curX -- as that is the node that is to be swapped:
if currX.data == currY.data:
return
The if condition that follows after it, is not useful at this point:
if (currX is None or currY is None): return
...as we already referenced swap.data (i.e. currY.data) at this point. So if this really were possible, it should be checked before referencing currY.data or currX.data. So combine it with the previous if:
if currX is None or currY is None or currX.data == currY.data:
return
The main loop in progress will skip a node when two consecutive(!) nodes are swapped. In that case, before the call to vymen, node will become currX that will hop over node.next. And so after the call, node will still reference the same node, but its position in the list is one further. By consequence, there is a node that will not haven an iteration of the loop. This is a cause of getting wrong outputs, even with plain numbers. To solve this, you could adapt vymen so it will return the node that will be at the same position as node was at the start of the call. The caller should then re-assign that reference to its node variable.
Some other remarks:
In Python it is not needed to put parentheses around if conditions
In vymen, the following condition should always be true:
if (prevX is not None): prevX.next = currY
else: self.front = currY
This should never be true, because you have designed your linked list to have a sentinel node at its front (having value None). So you don't even want self.front = currY to ever execute! prevX should never be None. It never is None in the way you call this function either. So just remove this construct and just assign:
prevX.next = currY
The same should happen with the if..else that follows it.
The vymen shoudn't need its last argument (swap), as it should always be the value of prev.next. So the function could start like this:
def vymen(self, first, prev):
prevX, currX, prevY, currY = first, first.next, prev, prev.next
And the call would be (note also the assignment of the return value):
node = self.vymen(first, node)
It is a pity that right after the function header of vymen, you define new variables for exactly the same as first and prev: prevX and prevY. Why not name the parameter variables like that immediately?
It is a pity that in progress you have an if with a break as the first statement in the while True body. You can better rewrite this so to have the condition in the while statement
Taking it all together, this is the resulting code for the two methods that had problems:
def progress(self, first, reverse):
if first is None or first.next is None:
return
node = first.next
while node.next: # Use a condition to exit instead of break
if reverse:
if first.next.data < node.next.data:
# Take the return value, and pass one argument less
node = self.vymen(first, node)
else:
if first.next.data > node.next.data:
# Take the return value, and pass one argument less
node = self.vymen(first, node)
node = node.next
def vymen(self, prevX, prevY): # Use the desired variable names
# No need for the swap argument
currX, currY = prevX.next, prevY.next
# Corrected comparison of the two nodes' data, and put the
# conditions in the right order:
if currX is None or currY is None or currX.data == currY.data:
return prev # return the node at the position of prev
if currX != prevY:
# Make the following assignments unconditionally
prevX.next = currY
prevY.next = currX
temp = currX.next
currX.next = currY.next
currY.next = temp
# Return the reference of the node that is in the position
# of prev: it is still in the same position:
return prev
else:
temp = currY.next
currX.next = temp
currY.next = currX
prevX.next = currY
# Return the reference of the node that is in the position
# of prev: it has moved, so now it is...:
return currY

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.

Pattern for multiple while loop exit conditions in Python

I spent some time looking through similarly named questions on here, but none seem to address the question of what to do if you have multiple exit conditions, each with their own logical path after exit. Here's what I mean.
I got started thinking about this while writing a function to add an element to a unique-element linked list:
class Node:
def __init__(self, key, next=None, count=1):
self.key = key # Unique key corresponding to this node
self.next = next # Reference to next node in linked list
self.count = count # Occurrences of key above in data
node = get_prepopulated_list()
# Iterate over linked list until element is found or list is exhausted
while node.key != key and node.next is not None:
node = node.next
# If found element, increment counter for that element
if node.key == key:
node.count += 1
# Else, add new node to end
else:
node.next = Node(key)
Notice how at least one of the two exit conditions is checked again after exit, leading to code duplication. Sure, we could flip which condition is being checked, but still there will remain code duplication.
In this case, we could use something like this, which might be a bit cleaner:
node = get_prepopulated_list()
# Iterate over linked list until element is found or list is exhausted
while node.key != key and node.next is not None:
try:
node = node.next
# Break out on 'None' condition
except AttributeError:
break
# Use implicit 'None' check from try-except to add new node to end on None
else:
node.next = Node(key)
return
# Otherwise, found element, so increment counter for that element
node.count += 1
But this isn't really any better, first because it adds a required escape during the else of the while-else, second because it adds some bloated error handling inside the loop, and finally because it breaks rule 2 of the Zen of Python: "Explicit is better than implicit". Beyond that, it's also not extensible to more than 2 exit conditions.
So I have two questions:
Is there a better way of handling such a case with 2 exit conditions and matching logical paths?
Is there a better way of handling the case with 3 or more exit conditions and matching logical paths?
while node.next is not None:
if node.key != key:
node = node.next
else:
node.count += 1
return
node.next = Node(key)
Note: will work only if get_prepopulated_list() is not None
Minimal, Reproducible Example
class Node:
def __init__(self,key):
self.key = key
self.next = None
self.count = 1
class LinkedList:
def __init__(self):
self.head = None
def add(self,key):
if self.head is None:
self.head = Node(key)
else:
node = self.head
while node.next is not None:
if node.key != key:
node = node.next
else:
node.count += 1
return
node.next = Node(key)
def show(self):
node = self.head
while node is not None:
print(node.key,'-->',node.count)
node = node.next
ll = LinkedList()
ll.add(5)
ll.add(6)
ll.add(2)
ll.add(5)
ll.add(7)
ll.show()

Implementing an insert method on a linked list on 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

Python 3 Recursive LinkedList Node Insertion

I'm trying to create a recursive function to insert into a linked list at a certain position to solve a hackerrank challenge, as part of educating myself in python. However, (according to hackerrank) it seems to be incorrect. Could someone help explain where I might be going wrong?
"""
Insert Node at a specific position in a linked list
head input could be None as well for empty list
Node is defined as
class Node(object):
def __init__(self, data=None, next_node=None):
self.data = data
self.next = next_node
return back the head of the linked list in the below method.
"""
def InsertNth(head, data, position):
# if we've found our position, then insert Node with pointer to next/None
if head == None or position == 0 :
return Node(data, head)
# if we're at the next to last position, then update it's pointer to our new Node
elif position == 1 :
head.next = Node(data, head.next)
# otherwise, step position down, and perform recursion
else :
InsertNth(head.next, data, position-1)
I've put in comments to try to explain what I'm understanding to be my logic so if anything's not clear, please let me know.
Posted a solution that was found acceptable by hackerrank:
def InsertNth(head, data, position):
# if we have found our position, then create a new Node
if head == None or position == 0 :
return Node(data, head)
else :
next_node = head
while position > 1 :
next_node = next_node.next
position -= 1
next_node.next = Node(data, next_node.next)
return head
Here is my implementation of it. Hope it helps. Feel free to ask questions if there's anything you're unsure about.
def InsertNth(head: Optional[Node], position: int, data: Optional[Node]) -> Optional[Node]:
"""Returns a new list where the second list is spliced into, or inserted at, given an index."""
if (position > 0):
if (head is not None):
return Node(head.data, splice(head.next, position - 1, data))
else:
if (data is not None):
if (data.next is not None):
return Node(data.data, splice(head, position, data.next))
else:
return Node(data.data, head)
else:
return None
else:
# If position <= 0, splice data list at start of head list
# This is the same process whether the position < 0 from the start or becomes < 0.
if (data is not None):
if (data.next is not None):
return Node(data.data, splice(head, position - 1, data.next))
else:
return Node(data.data, head)
else:
if (head is not None):
return head
else:
return None
Since you asked for recursive solution. Here is my implementation which got accepted.
def insertNodeAtPosition(llist, data, position):
# Write your code here
if(llist==None or position<0):
return llist
if(position==0):
node = SinglyLinkedListNode(data)
node.next = llist
return node
llist.next = insertNodeAtPosition(llist.next,data,position-1)
return llist

How to delete a given node from a linked list using Python

I am trying to learn linked list in Using python,
Could someone please guide me how to delete a particular give node from a linked list?
#!/usr/bin/python
class Node(object):
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def __str__(self):
return str(self.data)
def print_list(node):
while node:
print node,
node = node.next
print
def delete_node(node, node_to_remove):
if first_node == None:
return
pass
# way of creating linked list
def create_linked_list1(n):
linked_list = Node(1)
head = linked_list
for i in range(1, n):
head.next = Node(i)
head = head.next
return linked_list
node1 = create_linked_list1(10)
print_list(node1)
The obvious solution is this:
def delete_by_index(node, index):
for _ in range(index):
prev_node, node = node, node.next
prev_node.next = node.next
However, this won't be able to delete the first node. In order to do that, you need to make it return the new list head, which will normally be the old list head, but will instead be the old second node in the case where you deleted the head. So:
def delete_by_index(node, index):
if not index:
return node.next
head = node
for _ in range(index):
prev_node, node = node, node.next
prev_node.next = node.next
return head
It should be obvious how to simplify this, and you should do so.
Another option is to factor out the "searching" and "deleting" parts. Write an nth function (this should be easy), then you can do this:
def delete_by_index(node, index):
if not index:
return node.next
prev_node = nth(node, index-1)
prev_node.next = prev_node.next.next
return node
If you know about recursive functions, you might also want to figure out how to write either delete_by_index or nth recursively (it's one of the easiest recursive functions to write).
You may also want to trap the error caused by deleting, say, the 15th node of a 10-node list and make it something nicer. Figure out what error you would get in that case (or, if you can't figure it out, just run it and see), then try/except that, and raise an IndexError instead.
While you're at it, you may want to add a delete_by_data(node, data) function and maybe a delete_by_identity(node, child_node) for further practice.
Assume the following singly linked list with a pointer, head, to the first node
head ⇢ n0 → n1 → … → n i - 1 → n i → n i + 1 → … → n N-1 → None
def delete(i):
if i == 0: # there is no prev node, increment head
head = head.next
else:
prev = get_node(i-1) # find prev node, node[i-1]
prev.next = prev.next.next # remove node i
Because this is a singly linked list get_node(i-1) will have to start at head and increment i-1 times to find the node.
NOTE: If this was a doubly linked list, given node[i] you can find node[i-1] using node[i].prev.
v-> w-> x->y> z and if we want to delete x so that new Linked List is v -> w-> y-> z and we have access to only x
position of the next node to x i.e. position of the Node Y
next_node = x.next
swapping the Y to X and then deleting Y `x.data = next_node.data
x.next = next_data.next
Deletion of a node using python in single linked list.
def delete(self,data):
if self.head.data==data:
temp=self.head.next
del self.head
self.head=temp
else:
p=self.head
while p.next.data!=data:
p=p.next
temp=p.next.next
del p.next
p.next=temp
Delete by value:
def delete(head, val):
if head is None:
return None
prev = head
temp = head.next
while temp is not None:
if temp.val == val:
prev.next = temp.next
return val
else:
prev = temp
temp = temp.next
return None

Categories