Linked list implementation in python - python-3.x

I am learning how to use linked lists, and would like to add a value, remove a value, and test if a value is in the linked list. I am struggling to work out how test for a value and remove a value.
class Node(object):
def __init__(self, v, n):
self.value = v
self.next = n
class LinkedList(object):
def __init__(self):
self.firstLink = None
def add (self, newElement):
self.firstLink = Node(newElement, self.firstLink)
def test(self, testValue):
def remove(self, testValue):

To test if a value is in a LinkedList you have to go through the list and check every item
def contains(self, testValue):
ptr = self.firstLink
while ptr != None:
if ptr.value == testValue:
return True
ptr = ptr.next
return False
When using remove() method you usually don't pick an item to be removed. Remove method should only remove the last item added to LinkedList. Last in, First out.
def remove(self):
if self.firstLink == None():
return None
else:
item = self.firstLink.value
self.firstLink = self.firstLink.next
return item
To learn more about Linked Lists or see how can 'remove element' from LinkedList be implemented in python go to this site. It is well explained in there LinkedList

Related

code should not return any node object(Element)

https://gist.github.com/manaidu-2002/3f7eb60b8521201eba6548ca23cec053
Code returning Node Object(Element), please check the test cases and help me with this
"""Add a couple methods to our LinkedList class,
and use that to implement a Stack.
You have 4 functions below to fill in:
insert_first, delete_first, push, and pop.
Think about this while you're implementing:
why is it easier to add an "insert_first"
function than just use "append"?"""
class Element(object):
def __init__(self, value):
self.value = value
self.next = None
class LinkedList(object):
def __init__(self, head=None):
self.head = head
def append(self, new_element):
current = self.head
if self.head:
while current.next:
current = current.next
current.next = new_element
else:
self.head = new_element
def insert_first(self, new_element):
"Insert new element as the head of the LinkedList"
new_element.next = self.head
self.head = e_insert
def delete_first(self):
"Delete the first (head) element in the LinkedList as return it"
temp = self.head
if temp == None:
return None
s= temp
self.head = temp.next
return s
class Stack(object):
def __init__(self,head=None):
self.ll = LinkedList(head)
def push(self, new_element):
"Push (add) a new element onto the top of the stack"
temp = self.ll.head
while temp.next :
temp = temp.next
temp.next = new_element
def pop(self):
"Pop (remove) the first element off the top of the stack and return it"
if self.ll.head.next == None :
temp = self.ll.head
e= temp
temp = None
return e
elif self.ll.head.next:
temp = self.ll.head
while temp.next.next:
temp = temp.next
e= temp.next
temp.next = None
return e
return None
# Test cases
# Set up some Elements
e1 = Element(1)
e2 = Element(2)
e3 = Element(3)
e4 = Element(4)
# Start setting up a Stack
stack = Stack(e1)
# Test stack functionality
stack.push(e2)
stack.push(e3)
print(stack.pop().value)
print(stack.pop().value)
print(stack.pop().value)
print(stack.pop())
stack.push(e4)
print(stack.pop().value)
First of all, there is an unused name reference in your code: e_insert. This should read new_element.
The main issue is that your Stack class is not re-using the code you already have in your LinkedList class. In the new code you have written there are several mistakes in how you deal with next, but taking a step back, you are making a critical mistake in thinking that the top of the stack should be at the tail of the linked list, but that is very inefficient. The top of the stack should be at the head of the linked list. It is at that side that you can easily remove and insert elements without having to iterate the list.
So take these points into consideration:
Reuse the code you already have for LinkedList. In other words, call the methods defined on the LinkedList class.
The top of the stack is at the head of the linked list.
That means the Stack class can be as simple as this:
class Stack(LinkedList):
def __init__(self, head=None):
self.ll = LinkedList(head)
def push(self, new_element):
self.ll.insert_first(new_element)
def pop(self):
return self.ll.delete_first()

Wrapper Class in Python for Linked List

I am absolutely new to Wrapper Classes in Python. I was trying to implement it in a program of linked list where multiple linked list are in use. My code is:
def nodewrap(cls):
class Nodewrap:
def __init__(self):
self.head = None
self.tail = None
return Nodewrap
#nodewrap
class Node:
def __init__(self,data):
self.data = data
self.next = None
class Intersection:
def addnode(self,d):
newnode = Node(d)
if head == None:
head = tail = newnode
else:
tail.next = newnode
tail = newnode
obj1 = Intersection()
obj2 = Intersection()
obj3 = Intersection()
s1 = int(input("Enter size of 1st list : \n"))
for i in range(s1):
obj1.addnode(int(input("Enter the data : \n")))
s2 = int(input("Enter size of 1st list : \n"))
for i in range(s2):
obj2.addnode(int(input("Enter the data : \n")))
temp1 = obj1.head
for i in range(s1):
temp2 = obj2.head
for j in range(s2):
if temp1.data == temp2.data:
obj3.addnode(temp1.data)
break
temp2 = temp2.next
temp1 = temp1.next
print("Intersection is :")
temp = obj3.head
while temp!=None:
print(temp.data,end=" ")
temp = temp.next
I thought of using a wrapper class to wrap the class Node instead of using objects of the class Intersection only with data fields as head, tail. But it is giving me some sort of error with regards to init().
Please help.
I was trying to learn it from here:
https://www.geeksforgeeks.org/wrapper-class-in-python/
I think I understand what you want to do, but I think that you don't want to use a decorator, but you want to inherit from NodeWrap class
class Nodewrap:
head = None
tail = None
class Node(NodeWrap):
def __init__(self,data):
self.data = data
self.next = None
But I don't see any reason why to inherit this way. This should be enough, for a linked list. I have added is_first and is_last property
from __future__ import annotations
class Node:
prev_node = None
next_node = None
def __init__(self, data):
self.data = data
def add_node(self, node: Node) -> None:
if self.prev_node is not None:
raise ValueError('Previous node already defined.')
self.next_node = node
node.prev_node = self
#property
def is_first(self) -> bool:
return self.prev_node is None
#property
def is_last(self) -> bool:
return self.next_node is None
You can implement next, iter and create an Iterator class.
I don't recommend using next as the variable name.
from __future__ import annotations reference here. It's just for self reference annotation.

Removing element from container class

I'm having trouble to define the __delitem__ or similar method for the class Container in the example below. How can I rectify this? Thx.
import numpy as np
import pandas as pd
class XLData(object):
def __init__(self, name):
self.name = name
self.data = pd.DataFrame({self.name: list("ASDF" * 2),
'x': np.random.randint(1, 100, 8) })
def __repr__(self):
return repr(self.data.head(2))
class Container(object):
def __init__(self):
self.counter = 0
self.items = []
def append(self, item):
self.counter += 1
self.items = self.items + [item]
def __delitem__(self, name):
for c in self.items:
print("element name:{}, to delete:{}".format(c.name, name))
if c.name == name:
pass #!
#del c
def __iter__(self):
for c in self.items:
yield c
a = XLData('a')
b = XLData('b')
c = XLData('c')
dl = Container()
dl.append(a)
dl.append(b)
dl.append(c)
del dl['b']
for c in dl:
print(c)
# 'b' is still in ..
It is a good idea not to modify the array we are looping over in the loop itself. So just pick the index of the item and delete it outside the loop.
class Container(object):
def __init__(self):
self.counter = 0
self.items = {} # create a dict!
def append(self, item):
self.counter += 1
self.items[item.name] = item # add items to it, keyed under their names
def __delitem__(self, name):
del self.items[name] # this becomes *really* simple, and efficient
def __iter__(self):
for c in self.items.values: # loop over the dict's values to the items
yield c #
As you seem to be aware, given your code's comments, you can't usefully do del c in your loop, because that only removes the c variable from the function's local namespace temporarily, it doesn't change the list structure at all.
There are a few different ways you could make it work.
One idea would be to use enumerate while looping over the values in the list, so that you'll have the index at hand when you need to delete an item from the list:
for i, item in enumerate(self.items):
if item.name == name:
del self.items[i]
return
Note that I return from the function immediately after deleting the item. If multiple items with the same name could exist in the list at once, this may not be what you want, but this code can't properly handle that case because once you delete an item from the list, the iteration won't work properly (it will let you keep iterating, but it will have skipped one value).
A better option might be to rebuild the list so that it only includes the values you want to keep, using a list comprehension.
self.items = [item for item in self.items if item.name != name]
That's nice and concise, and it will work no matter how many items have the name you want to remove!
One flaw of both of the approaches above share is that they'll be fairly slow for large lists. They need to iterate over all the items, they can't tell ahead of time where the item to remove is stored. An alternative might be to use a dictionary, rather than a list, to store the items. If you use the item names as keys, you'll be able to look them up very efficiently.
Here's an implementation that does that, though it only allows one item to have any given name (adding another one will replace the first):
class Container(object):
def __init__(self):
self.counter = 0
self.items = {} # create a dict!
def append(self, item):
self.counter += 1
self.items[item.name] = item # add items to it, keyed under their names
def __delitem__(self, name):
del self.items[name] # this becomes *really* simple, and efficient
def __iter__(self):
for c in self.items.values(): # loop over the dict's values to the items
yield c
It is a good idea not to modify the array we are looping over in the loop itself. So just pick the index of the item and delete it outside the loop.
def __delitem__(self, name):
idx = -1
found = False
for c in self.items:
idx += 1
print("element name:{}, to delete:{}".format(c.name, name))
if c.name == name:
found = True
break
if found:
del self.items[idx]
The way you have it implemented, it's going to be slow for operations like del. And if you wanted to add other methods that return your objects by name like __getitem__(), looking them up by iterating through a list will be slow. You probably want a dictionary to hold your XLData objects in inside Container. And you won't be needing to keep a count of them since the data objects of Python all have a length property.
class Container(object): # Python 3 doesn't require 'object' in class decls.
def __init__(self):
self._items = {}
def add(self, item):
# self._items.append(item) # Why create a new list each time.
# Just append.
self._items[item.name] = item
def __len__(self):
return len(self._items)
def __getitem__(self, name):
return self._items[name]
def __delitem__(self, name):
del self._items[name] # Simple.
def __iter__(self):
for c in self._items.values():
yield c
With a dict you get the benefits of both a list and a dictionary: fast access by name, and iteration over items, etc. The dict keeps track of the order in which the keys and items are added. It is possible to have more than one data type holding information on your contained objects if you really needed a separate list to sort and iterate over. You just have to keep the dict and list in sync.
Come to think of it, you could even sort the dictionary without a list if you wanted your class to support a sort() operation, just requires a little creativity.
def sort(self, key=None):
self._items = {k: v for k, v in sorted(self._items.items(), key=key)}
I think I'm taking it a bit too far now =)
alternative method, using list filter option with object attr conditions in def delitem method
import numpy as np
import pandas as pd
class XLData(object):
def __init__(self, name):
self.name = name
self.data = pd.DataFrame({self.name: list("ASDF" * 2),
'x': np.random.randint(1, 100, 8)})
def __repr__(self):
return repr(self.data.head(2))
class Container(object):
def __init__(self):
self.counter = 0
self.items = []
def append(self, item):
self.counter += 1
self.items = self.items + [item]
def __delitem__(self, name):
self.items = [x for x in self.items if x.name != name]
def __iter__(self):
for c in self.items:
yield c
a = XLData('a')
b = XLData('b')
c = XLData('c')
dl = Container()
dl.append(a)
dl.append(b)
dl.append(c)
del dl['b']
for c in dl:
print(c)
output
a x
0 A 13
1 S 97
c x
0 A 91
1 S 17

Is there one variable way to create single linked list in python

So I have the below single linked list class:
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
and now I create a single linked list from an array [-10,-3,0,5,9]
input = [-10,-3,0,5,9]
head = ListNode(input[0])
for idx, x in enumerate(input[1:]):
if idx == 0:
head.next = ListNode(x)
temp = head.next
else:
temp.next = ListNode(x)
temp = temp.next
Let's check my implementation:
while (head):
print(head.val)
head = head.next
the output is
-10
-3
0
5
9
now the output is correct. But as you saw, I used some ugly way of creating a single linked list instance, i.e., I used two variables: temp and head. Is there any way I can use only head or temp?
Here is a decent implementation of a singly linked list, with a separate class for ListNode and SinglyLinkedList, in which the ListNode is a node in a singly-linked list whereas SinglyLinkedList is the singly-linked list which also supports the operations like prepend and append.
class ListNode:
"""
A node in a singly-linked list.
"""
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def __repr__(self):
return repr(self.data)
class SinglyLinkedList:
def __init__(self):
"""
Create a new singly-linked list.
Takes O(1) time.
"""
self.head = None
def __repr__(self):
"""
Return a string representation of the list.
Takes O(n) time.
"""
nodes = []
curr = self.head
while curr:
nodes.append(repr(curr))
curr = curr.next
return '[' + ', '.join(nodes) + ']'
def prepend(self, data):
"""
Insert a new element at the beginning of the list.
Takes O(1) time.
"""
self.head = ListNode(data=data, next=self.head)
def append(self, data):
"""
Insert a new element at the end of the list.
Takes O(n) time.
"""
if not self.head:
self.head = ListNode(data=data)
return
curr = self.head
while curr.next:
curr = curr.next
curr.next = ListNode(data=data)
if __name__ == '__main__':
singly_linked_list = SinglyLinkedList()
print(singly_linked_list)
input_array = [-10, -3, 0, 5, 9]
for x in input_array:
print(x)
singly_linked_list.append(x)
print(singly_linked_list)
Other implementations for the singly-linked list can be seen here.
Hope it helps.

Stack pop using linked list - Python

I am trying to pop an item off a stack (using a linked list as oppose to an array). I first created a LinkedList class with 3 nodes with the values [1,2,3]. So I would like pop off the last node (node_C, value=3), therefore I expect to see the values [1,2]. Instead nothing prints out.
class LinkedList:
def __init__(self, value):
self.value = value
self.next = None
node_A = LinkedList(1)
node_B = LinkedList(2)
node_C = LinkedList(3)
node_A.next = node_B
node_B.next = node_C
def pop(head):
current_node = head
while current_node.next:
if current_node.next == None:
break
else:
current_node = current_node.next
del current_node
return node_A.value, node_B.value, node_C.value
try:
print(pop(node_A))
except NameError:
pass
How can I rewrite this to achieve my desired results (i.e. show values 1,2 .. with 3 popped off)?
The del current node and return node_A.value, node_B.value, node_C.value
commands should belong to the pop function, so they should be intended. But anyway the del current node doesnt work for me. Instead you could write current_node.value = None but then you still return all 3 node values so the result would be 1,2,None.
I would rather write the pop function inside the class and add another printlist function to the class as well. The pop function just removes the last element from the list (changes the next attribute to None for the 2nd last element in the list) and doesn't print or return anything. The printlist function iterates through the list and prints out all elements (while there are next elements). Here is my code:
class LinkedList:
def __init__(self, value):
self.value = value
self.next = None
def pop(self):
current_node = self
while current_node.next:
if current_node.next.next == None:
current_node.next = None
else:
current_node = current_node.next
def printlist(self):
current_node = self
lst = [current_node.value]
while current_node.next:
current_node = current_node.next
lst.append(current_node.value)
print lst
node_A = LinkedList(1)
node_B = LinkedList(2)
node_C = LinkedList(3)
node_A.next = node_B
node_B.next = node_C
try:
node_A.pop()
node_A.printlist()
except NameError:
pass
If I run this, the result is [1,2]. If I remove the node_A.pop() I get [1,2,3]. If I write another node_A.pop() then result is [1]
I guess I have found the issue with your logic, so based on the code provided it seems that pop function doesn't return anything, may be it's just formatting or something else.
But here is the correct version of your code, where I just delete the last node in the pop method and I call another method called listValues which returns me with the node values that exist in the linked list after pop
Look at the below implementation for a clearer view.
class LinkedList:
def __init__(self, value):
self.value = value
self.next = None
node_A = LinkedList(1)
node_B = LinkedList(2)
node_C = LinkedList(3)
node_A.next = node_B
node_B.next = node_C
def pop(head):
current_node = head
while current_node.next:
if current_node.next == None:
del current_node
break
else:
current_node = current_node.next
def listValues(head):
values = []
current_node = head
while current_node.next:
values.append(current_node.value)
current_node = current_node.next
return values
try:
pop(node_A)
print(listValues(node_A))
except NameError:
pass
Hope this helps!

Resources