Linked Queue ADT implementation, Lost node - python

When I implemet the dequeue() method, and in the following code
def dequeue(self):
if self.is_empty():
print("The Queue is empty")
return
answer = self._head._element
#---------------------
self._head = self._head._next #here, what happens with the "lost" node, because i think it is lost by the linked list
#---------------------
self._size -= 1
if self.is_empty():
self._tail = None
return answer
Is that node retrieved by the garbage collector?, because when i lost the reference to that node, and i have no way to access it.
The node is implemented as following within a LinkedList class.
class _Node:
__slots__ = "_element","_next"
def __init__(self, element, next):
self._element = element
self._next = next

Yep, it's a notation, and the garbage collector knows when to retrieve unused data.

Related

Why creating an instance of a class within a class method changes the 'self' argument?

I'm writing a linked list in Python and I've come across an issue which is really troublesome and terrible for debugging and I feel like I'm missing something about Python. I'm supposed to create a singly linked list with some basic functionalities. One of them is the take() function, which is meant to create a new list of n first elements of the original list.
However, creating a new instance of the LinkedList class seems to change the .self parameter and the variable node is modified, as the attribute .next is turned to None. In result, when creating a list and then trying to make a new one out of the n elements of it, the program runs indefinitely, but no matter which part I look at, I cannot find the loop or the reason behind it.
class LinkedList:
def __init__(self, head=None):
self.head = head
def is_empty(self):
if self.head == None:
return True
else:
return False
def add_last(self, node):
if self.is_empty():
self.head = node
return
nextEl = self.head
while True:
if nextEl.next is None:
nextEl.next = node
return
nextEl = nextEl.next
def take(self, n):
node = self.head
newHead = self.head
newHead.next = None
newList = LinkedList(newHead)
count = 0
while count < n:
newList.add_last(node.next)
node = node.next
count += 1
return newList
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
Thank you for all your help.
In the take() function the line
newHead.next = None
modifies a node of the linked list, breaking this list. You can fix this as follows:
def take(self, n):
node = self.head
newHead = Node(self.head.data)
newList = LinkedList(newHead)
count = 0
while count < n:
newList.add_last(node.next)
node = node.next
count += 1
return newList
This, however, still will not work correctly since there is a problem with the add_last() function too. This function, I think, is supposed to add a node as the last element of a linked list, but since you do not modify the next attribute of the node, you actually append a whole linked list starting with that node. This can be fixed in the following way:
def add_last(self, node):
if self.is_empty():
self.head = node
return
nextEl = self.head
while True:
if nextEl.next is None:
nextEl.next = Node(node.data)
return
nextEl = nextEl.next
There are more issues. For example, take(sefl, n) will actually create a list of n+1 elements and will throw an exception if it is applied to a linked list that does not have that many elements.

My self-defined queue not printing properly

I'm currently doing a class assignment to create a Class Queue using linked list. My code is as follows:
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class Queue:
def __init__(self):
self.front = None
self.rear = None
def enqueue(self, value):
newNode = Node(value)
if self.rear is None:
self.front = newNode
self.rear = self.front
else:
self.rear.next = newNode
self.rear.next.prev = self.rear
self.rear = self.rear.next
def dequeue(self):
if self.front is None:
return None
else:
to_remove = self.front.data
self.front = self.front.next
self.front.prev = None
return to_remove
def printQueue(self):
print('The elements in the queue are ')
element = self.front
while element is not None:
print(element, end = "")
element = element.next
myqueue = Queue()
for i in range(21):
if i % 3 == 0:
myqueue.enqueue(i)
elif i % 5 == 0:
myqueue.dequeue()
myqueue.printQueue()
But when I tried printing, it comes up like this:
<__main__.Node object at 0x000001EB465D8048><__main__.Node object at 0x000001EB465D8128><__main__.Node object at 0x000001EB465D8080><__main__.Node object at 0x000001EB465D80F0>
I searched on the internet and tried to change my code but it still shows up the same. I don't understand why though.
As the comment says, and I modified your questions itself, too. You have to print the value of the objects, not the objects themselves. It's not an error, just a little confusion on your part.
When you print out element you print the location of the object's memory.
<__main__.Node object at 0x000001EB465D8048><__main__.Node object at 0x000001EB465D8128><__main__.Node object at 0x000001EB465D8080><__main__.Node object at 0x000001EB465D80F0>
The hexadecimal values are addresses.
In order to access the value present at these locations use element.data instead of element.
First of all, that is not an error. Your printQueue function iterates the elements and prints each element. The type of each element is Node.
For example, your output contains the representation of the nodes:
The object below is a single node, defined in the __main__ function. Its type is Node Object and its memory address is 0x000001EB465D8048
<__main__.Node object at 0x000001EB465D8048>
To print a user friendly version of each node, the node class needs to override the __str__ method.
For example, if you would modify your Node class to have the following structure, then you could just use print(element, end = "\n") inside your printQueue method.
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
def __str__(self):
return str(self.data)
The output then would be
The elements in the queue are
9
12
15
18
Furthermore, while not directly related to your question, it's tangential enough that you might find it helpful. Python objects have both a __str__ and a __repr__method.
Simply put, the __str__ is used to represent the readable form of your object, and the __repr__ the unambiguous representation.
For more information on those 2 methods, this stackoverflow post does a good job at explaining them.

dequeue in circularly linked list in python

I am working on the implementation of Queue with a circularly linked list in python. Below is the pictorial representation of circularly LinkedList
I was able to implement most of the code except the dequeue routine. In dequeue routine, one has to keep track of the previous node reference as well as the next node reference with respect to current node. In double linked list, it's easy to implement. However, I have no idea how to implement this concept in single linked list.
class CircularQueue:
''' Queue implementation using a Circularly linked list for storage '''
class _Node:
__slots__ == '_element','_next'
def __init__(self,element,next):
self._element = element
self._next = next
def __init__(self):
'''Create an empty queue'''
self._current = None
self._size = 0
def __len__(self):
return self._size
def is_empty(self):
return self._size == 0
def enqueue(self,e):
node = self._Node(e,None)
if self.is_empty():
newest._next = newest
else:
curr_node = self._current._next
node._next = curr_node
self._current = node
self._size += 1
def dequeue(self):
if self.is_empty():
raise Empty('Stack is empty')
It would be more helpful if anyone can give me thoughts on how to move forward in dequeue routine.
Since your linked list is unidirectional, an element does only have a reference to it's successor, but never to it's predecessor, which you would need both in order to easily close the hole in the list once you dequeue an element.
I see two possibilities: You could either make the linked list bidirectional, therefore adding a reference to the previous element. I cleaned up your implementation a bit, I hope you still can make something of this:
""" Queue implementation using a Circularly linked list for storage """
class _Node:
def __init__(self, element, next=None, previous=None):
self.element = element
if next is None:
next = self
self.next = next
if previous is None:
previous = self
self.previous = previous
class CircularQueue:
def __init__(self):
self._current = None
self._size = 0
def __len__(self):
return self._size
def get_head(self):
return self._current.element
def is_empty(self):
return self._size == 0
def next(self):
self._current = self._current.next
return self._current.element
def previous(self):
self._current = self._current.pevious
return self._current
def enqueue(self, element):
""" Adds an element at the current position, ahead of the current element """
if self.is_empty():
new_node = _Node(element)
else:
new_node = _Node(element, self._current.next, self._current)
self._current.next = new_node
self._current = new_node
self._size += 1
We can now check if our code is correct:
cq = CircularQueue()
cq.enqueue("A")
cq.enqueue("B")
cq.enqueue("C")
print(cq.get_head())
print(cq.next())
print(cq.next())
print(cq.next())
And you will see C, A, B and C printed in succession.
A bilateral queue enables us to implement a dequeue method like this:
def dequeue(self, element_key=None):
if self.is_empty():
raise KeyError('Stack is empty')
if element_key is None:
self._current.next.previous = self._current.previous
self._current.previous.next = self._current.next
return self.next()
else:
current = self._current
while self._current.element != element_key:
self.next()
if self._current == current:
raise KeyError('Element not found')
self.dequeue()
And if we test it...
print("dequeuing 'B'")
cq.dequeue("B")
print(cq.get_head())
print(cq.next())
print(cq.next())
print(cq.next())
... we should see that "dequeuing 'B'" and C, A, C & again A are printed, which makes us happy. :)
Using a unilateral approach is possible too; you might end up having less work handling references while you would have run through your entire circle (in the worst case). First, you'd skip to the next element in the queue until the next element of the current one's is the one you want to dequeue, then set the current's elements next reference to the dequeued one's next, and you are basically done.

Linked lists in Python

I have a Linked Lists assignment for school although I am just getting the hang of class constructors. I am trying to simply get the basics of the linked list data structure down, and I understand the basic concept. I have watched lots of Youtube tutorials and the like, but where I am failing to understand is how to print out the cargo or data in my nodes using a loop.
I have written something along these lines:
class Node:
def __init__(self, value, pointer):
self.value = value
self.pointer = pointer
node4 = Node(31, None)
node3 = Node(37, None)
node2 = Node(62, None)
node1 = Node(23, None)
Now...I understand that each node declaration is a call to the class constructor of Node and that the list is linked because each node contains a pointer to the next node, but I simply don't understand how to print them out using a loop. I've seen examples using global variables for the "head" and I've seen subclasses created to accomplish the task. I'm old and dumb. I was wondering if someone could take it slow and explain it to me like I'm 5. If anyone out there has the compassion and willingness to hold my hand through the explanation, I would be greatly obliged. Thank you in advance, kind sirs.
First of all, your nodes should be created something like this :
node4 = Node(31, node3)
node3 = Node(37, node2)
node2 = Node(62, node1)
node1 = Node(23, None)
Now, i am sure you can see that the last node in the list would point to None. So, therefore, you can loop through the list until you encounter None. Something like this should work :
printhead = node4
while True:
print(printhead.value)
if printhead.pointer is None:
break;
else :
printhead = printhead.pointer
This is a very basic linked list implementation for educational purposes only.
from __future__ import print_function
"""The above is needed for Python 2.x unless you change
`print(node.value)` into `print node.value`"""
class Node(object):
"""This class represents list item (node)"""
def __init__(self, value, next_node):
"""Store item value and pointer to the next node"""
self.value = value
self.next_node = next_node
class LinkedList(object):
"""This class represents linked list"""
def __init__(self, *values):
"""Create nodes and store reference to the first node"""
node = None
# Create nodes in reversed order as each node needs to store reference to next node
for value in reversed(values):
node = Node(value, node)
self.first_node = node
# Initialize current_node for iterator
self.current_node = self.first_node
def __iter__(self):
"""Tell Python that this class is iterable"""
return self
def __next__(self):
"""Return next node from the linked list"""
# If previous call marked iteration as done, let's really finish it
if isinstance(self.current_node, StopIteration):
stop_iteration = self.current_node
# Reset current_node back to reference first_node
self.current_node = self.first_node
# Raise StopIteration to exit for loop
raise stop_iteration
# Take the current_node into local variable
node = self.current_node
# If next_node is None, then the current_node is the last one, let's mark this with StopIteration instance
if node.next_node is None:
self.current_node = StopIteration()
else:
# Put next_node reference into current_node
self.current_node = self.current_node.next_node
return node
linked_list = LinkedList(31, 37, 62, 23)
for node in linked_list:
print(node.value)
This doesn't handle many cases properly (including break statement in the loop body) but the goal is to show minimum requirements for linked list implementation in Python.

Not understanding some composition aspects (not composition)

I am brushing up on some data structures and algorithms with Python, so I am implementing an Unordered Linked list. Within the same file I first wrote a Node class followed by a List class. What I don't get is how the "current" variable in my search_item() method seems to be a node object or at least able to access the Node class methods and attributes. I noticed that if I comment out my add_node() method then "current" no longer has access to Node's methods. Now I am not explicitly using neither inheritance nor composition, so I am having a hard time seeing how current just gets to call get_next() the way the code is written below. I would think I'd have to declare current as: current = Node(self.head) but just current = self.head seems to work?
Your help would be greatly appreciated.
class Node:
def __init__(self, data):
self.data = data
self.next = None
def get_data(self):
return self.data
def set_data(self, d):
self.data = d
def get_next(self):
return self .next
def set_next(self, n):
self.next = n
class UnorderedList:
def __init__(self):
self.head = None
def add_node(self, item):
tmp = Node(item)
tmp.set_next(self.head)
self.head = tmp
def search_item(self, item):
current = self.head
# current = Node(self.head)
found = False
while current != None and not found:
if current.get_data() == item:
found = True
else:
current = current.get_next()
return found
Well if you comment out add_node then you do not add nodes to your linked list any longer therefore search_item will always see the initial value of self.head which is None.
Calling current.get_next() just works because via add_node you always ensure that self.head points either to None or to an instance of Node as tmp is created by tmp = Node(item) and then assigned to self.head = tmp. Therefore when setting current = self.head it will already refer to an instance of Node (or None) so you do not need to call current = Node(self.head).
I just recently came across the concept of duck typing and this old post of mine came to mind. It seems that this is what's at play and just didn't understand it at the time. 'current' is set to None by default, by when invoking any of the methods defined in the Node class, it is automatically defined as Node object.

Categories

Resources