I was trying to implement a doubly linked list with the following functions:
_insertItemAfterNode(self,index)
_nodeBeforeIndex(self, index)
insertItemAtTheFront(self,item)
insertItemAtTheEnd(self,item)
insertItemAtIndex(self, index, item)
The last three functions should use the first two private methods.
This is what I have so far but I can't seem to work it. Any help is appreciated!!!
# DoublyLinkedLists
class Node(object):
def __init__(self, prev = None, data=None, next = None):
self._prev = prev
self._data = data
self._next = next
def __str__(self):
return str(self._data)
def __repr__(self):
return "Node(%s,%s,%s)" % (repr(self._prev), repr(self._data), repr (self._next))
def __eq__(self, other):
if other == None:
return False
else:
return self._prev == other._prev and self._data == other._data and self._next == other._next`enter code here`
class DoublyLinkedList(object):
def __init__(self):
self._first = Node(None, None, None)
self._length = 0
self._last = Node(None, None, None)
def __len__(self):
return self._length
def _insertItemAfterNode(self,item,aNode):
newNode = Node(aNode._prev, item, aNode._next)
aNode._next= newNode._prev
aNode._prev=newNode._next
self._length += 1
def _nodeBeforeIndex(self, index):
if 0 <= index <= self._length:
aNode = self._first
for i in range(index):
aNode = aNode._next
return aNode
else:
raise IndexError
def _nodeAfterIndex(self, index):
if 0<= index <= self.length:
aNode = self._last
for i in range(index):
aNode = aNode._prev
return aNode
else:
raise IndexError
def __getitem__(self, index):
return self._nodeBeforeIndex(index)._next._data
def insertAtEnd(self, item):
self._nodeAfterIndex(item)
def __iter__(self):
return DoublyLinkedListIterator(self)
def __setitem__(self, index, item):
self._nodeBeforeIndex(index)._next._data = item
def insertItemAtTheFront(self, item):
self._insertItemAfterNode(item, self._nodeBeforeIndex(0))
def insertItemAtTheEnd(self, item):
self._insertItemAfterNode(item, self._nodeBeforeIndex(self._length))
def insertItemAtIndex(self, index, item):
'''Valid range 0 <= index <= length.'''
self._insertItemAfterNode(item, self._nodeBeforeIndex(index))
def __iter__(self):
return DoublyLinkedListIterator(self)
def __repr__(self):
#used by print in abscence of __str__ .
plist = []
for item in self:
plist.append(item)
return "DoublyLinkedList(%s)" % str(plist)
class DoublyLinkedListIterator(object):
def __init__(self, aList):
self._list = aList
self._currentIndex = -1
self._currentNode = self._list._first
def __iter__(self):
return self
def __next__(self):
if self._currentIndex == self._list._length - 1:
raise StopIteration
else:
self._currentIndex += 1
self._currentNode = self._currentNode._next
return self._currentNode._data
def main():
x = DoublyLinkedList()
x.insertItemAtTheEnd(45)
print(x)
main()
I see two immediate issues with your code. There may be more issues that I haven't noticed, but these will probably prevent you from seeing any others until they are fixed.
The first issue is very simple. You have two sentinel nodes, _first and _last in your DoublyLinkedList class. However, they're not linked to each other, so when more nodes are added later, they won't be connected to both sentinels like they should be.
You can fix this by changing the __init__ method:
def __init__(self):
self._first = Node(None, None, None)
self._length = 0
self._last = Node(self._first, None, None) # pass first node as an arg here
self._first._next = self._last # make a link back too
The second issue is how you're adding nodes. There are a couple of places where you're assigning values from the _prev or _next attributes of a node, rather than referring to the node itself.
Lets think of a list containing A and C nodes, and you want to insert B in between them. You need to make B's node link to both A and C, and then modify C and A to link back to it. In your code, A is aNode, B is newNode and C is (at the start) aNode._next.
def _insertItemAfterNode(self, item, aNode):
newNode = Node(aNode, item, aNode._next) # link B to A and C
aNode._next._prev = newNode # link C back to B
aNode._next = newNode # and A to B too!
self._length += 1
There are a few things strange with your code. Let's dig into the first problem that I found:
def _insertItemAfterNode(self,item,aNode):
newNode = Node(aNode._prev, item, aNode._next)
aNode._next= newNode._prev
aNode._prev=newNode._next
self._length += 1
Assume that you have:
prevNode <-> aNode <-> nextNode
You now create a newNode which points to prevNode as its previous and nextNode as its follow-up. Then you reverse the pointers of aNode. prevNode still thinks its following node is aNode, but aNode now thinks that its following node is prevNode again.
/---------\
/----> aNode < v
prevNode <--------/ \-- nextNode
^ ^
\---- newNode --/
Which is not nearly what you want:
prevNode <-> aNode <-> newNode <-> nextNode
Here's a better version:
def _insertItemAfterNode(self,item,aNode):
# If the new Node follows aNode, than its preceeding node is aNode,
# not aNode._prev!
newNode = Node(aNode, item, aNode._next)
# Now we need to make aNode point at its new following Node, newNode
aNode._next = newNode
# And finally, we need to make the following Node notice that
# its preceeding Node has changed.
newNode._next._prev = newNode
self._length += 1
This doesn't care about any edge cases, for example what to do if aNode is the last node in the linked list.
What I recommend is that you take out a piece of paper and draw how the different links need to be changed for each of the operations that you need to make, because it is hard to keep all this in your head.
Make sure that you look at all the edge cases, ie. putting into an empty list, putting in item into the first and last positions.
And finally, while shuffling links around, always double check where each link is pointing at each instruction, and not to drop any nodes accidentally.
Related
It turned out to be an error as a time limit was exceeded,
but I've already raised the StopIteration...
I think I did something wrong for my iteration part, but it's really hard to find the error. The test output keeps running and even printed out the None value. How does it happen?
class LinkedListIterator:
def __init__(self, head):
self.__current = head.get_next()
def __iter__(self):
return self
def __next__(self):
if self.__current == None:
raise StopIteration
else:
item = self.__current.get_data()
self.__current = self.__current.get_next()
return item
These were the inputs I used to run the program:
my_list = LinkedListDLL()
my_list.add_to_head(1)
print("Contents:", end=" ")
for node in my_list:
print(node, end=" ")
print()
This code is meant to stop iteration when it reaches the head of the list.
if self.__current == None:
raise StopIteration
However, you represent the head with a NodeDLL object which is different from None.
You could keep a reference to the head and check against that instead:
class LinkedListIterator:
def __init__(self, head):
self._head = head
self._current = head.get_next()
def __iter__(self):
return self
def __next__(self):
if self._current is self._head:
raise StopIteration
else:
item = self._current.get_data()
self._current = self._current.get_next()
return item
What you want to implement is the API of a MutableSequence with the implementation of a doubly-linked-list.
To do that in Python, you should rely on collections.abc which can guide you through the process of implementing all required methods.
By example, a linked-list is actually a class inheriting from MutableSequence.
from collections.abc import MutableSequence
class LinkedList(MutableSequence):
pass
ll = LinkedList()
On instantiation of a class which has some abstract methods not yet written, you will get a TypeError which will guide you through which methods need to be implemented.
TypeError: Can't instantiate abstract class LinkedList with abstract methods __delitem__, __getitem__, __len__, __setitem__, insert
In particular, note that a list or a linked-list is not an iterator, it is an iterable. What this means is the __iter__ method should not return self and rely on __next__, it should instead return a brand new iterator on the content of the linked-list.
In other words, you can iterate only once through an iterator and multiple times through and iterable.
Full implementation
It turns out I have a full implementation of a doubly-linked-list implemented that way. You can have a look.
from collections.abc import MutableSequence
class LinkedList(MutableSequence):
class _Node:
def __init__(self, value, _next=None, _last=None):
self.value, self._next, self._last = value, _next, _last
def __str__(self):
return f'Node({self.value})'
def __init__(self, iterable=()):
self.start = None
self.last = None
empty = object()
iterable = iter(iterable)
first = next(iterable, empty)
if first is empty:
return
current = self._Node(first)
self.start, self.last = current, current
for value in iterable:
new_node = self._Node(value, _last=self.last)
self.last._next = new_node
self.last = new_node
def __len__(self):
if self.start is None:
return 0
else:
return sum(1 for _ in self)
def __iter_nodes(self):
current = self.start
while current is not None:
yield current
current = current._next
def __reversed_iter_nodes(self):
current = self.last
while current is not None:
yield current
current = current._last
def __iter__(self):
for node in self.__iter_nodes():
yield node.value
def __reversed__(self):
for node in self.__reversed_iter_nodes():
yield node.value
def __get_node(self, index):
if index >= 0:
for item in self.__iter_nodes():
if index == 0:
return item
index -= 1
else:
for item in self.__reversed_iter_nodes():
if index == 0:
return item
index += 1
raise IndexError
def __getitem__(self, index):
if index >= 0:
for item in self:
if index == 0:
return item.value
index -= 1
else:
for item in reversed(self):
if index == 0:
return item.value
index += 1
raise IndexError
def __setitem__(self, key, value):
self[key].value = value
def __delitem__(self, key):
node = self[key]
if node._last:
node._last._next = node._next
if node._next:
node._next._last = node._last
def insert(self, index, value):
if index > len(self):
self.last = self._Node(value, _last=self.last)
else:
where = self.__get_node(index)
_last = where._last
new_node = self._Node(value, _next=where, _last=_last)
if _last:
_last._next = new_node
else:
self.start = new_node
where._last = new_node
Example
ll = LinkedList(range(1, 5))
print(*ll)
print(*reversed(ll))
ll.insert(2, 'foo')
print(*ll)
Output
1 2 3 4
4 3 2 1
1 2 foo 3 4
I'm working on a project where I manipulate a lot of sorted lists of elements, and I need to be able to remove any of these quickly. Since I don't need any kind of indexation, I thought that the doubly linked list structure would be best. I couldn't find any good pre-made module so I made my own:
class Node: # nodes for doubly-linked lists
def __init__(self, val, dll):
self.val = val
self.next = None
self.prev = None
self.dll = dll
class DLList: # doubly-linked lists
def __init__(self):
self.first = None
self.last = None
self.len = 0
# def __iter__(self):
# self.curr = self.first
# return self
#
# def __next__(self):
# if self.curr == None:
# raise StopIteration
# self.curr = self.curr.next
# if self.curr == None:
# raise StopIteration
# return self.curr
def append(self, val): # add a node with value val at the end of the list
node = Node(val, self)
node.prev = self.last
self.last = node
if self.first == None: # <=> if self was empty
self.first = node
self.len += 1
def appendleft(self, val): # same as previous, but at the beginning of the list
node = Node(val, self)
node.next = self.first
self.first = node
if self.last == None:
self.last = node
self.len += 1
def nodeat(self, i): # gives the ith node (starting at 0)
if i == -1:
return None
if i > self.len or i < -1:
raise IndexError('index out of range')
curr = self.first
for j in range(i):
curr = curr.next
return curr
def remove(self, node): # remove a given node in the list
if node.dll != self: #cannot remove a node that is not in the list
raise ValueError('node not in list')
p = node.prev
n = node.next
v = node.val
node.dll = None
if p != None:
p.next = n
else:
self.first = n
if n != None:
n.prev = p
else:
self.last = p
self.len -= 1
return v
def add(self, val, i): # add a node at the ith place in the list
node = Node(val, self)
if i > self.len:
raise IndexError('index out of range')
self.len += 1
previ = self.nodeat(i)
node.prev = previ.prev
node.next = previ
previ.prev = node
def clear(self): # empty the list
self.first = None
self.last = None
self.len = 0
def extend(self, iterable): # add the elements of iterable in order at the end of the list
for i in iterable:
self.append(i)
self.len += 1
def extendleft(self, iterable): # same as previous, but at the beginning (and in reverse order)
for i in iterable:
self.appendleft(i)
self.len += 1
def dll_to_list(self): # return a python list with the elements of the doubly-linked list
res = []
curr = self.first
while curr != None:
res.append(curr.val)
curr = curr.next
return res
def is_empty(self): # check whether the list is empty
return self.len == 0
Since I would lose time checking that the item I want to remove is in the list by browsing it, I added a pointer toward the list a Node is in inside the node, so that I can check that I'm not removing things from the wrong list.
Those lists are stocked in a Python dictionary, and at some point I started getting 'node not in list' errors. Does anyone know how it could appear? I never use anything but the methods listed here to manipulate the lists...
Otherwise, does anyone know about a well-coded module that I could use in place of this one?
Thanks!
A doubly linked list has links going both directions.
Example:
def append(self, val): # add a node with value val at the end of the list
node = Node(val, self) # new node, ok
node.prev = self.last # ok, the new nodes prev is the last node of your list
self.last = node # ok, your new node is now the last of your list
if self.first == None: # yeah, ok, if its empty its also the first one now
self.first = node
self.len += 1
but ... you do not set the back-direction:
node.prev.next = node # before node.prev = self.last
Similar in your other appends. You have to always clear/reset/set all the links in both directions if you add/remove things into a doubly-linked-list:
( Red are all the changed variabels on append )
Essentially your list is not complete - if you operate / iterate on it, things will go missing in unexpected ways
I am trying to write a method that merges two sorted Doubly Linked Lists of integers, using recursion (it has to be recursive). The method creates and returns a new Doubly Linked List with the integer values in order. For example, if doublylinkedlist1 = [0-2-4] and doublylinkedlist2 = [1-3-5], the merge_sorted method would return [0-1-2-3-4-5].
However, when I run my code below:
class EmptyCollection(Exception):
pass
class DoublyLinkedList:
class Node:
def __init__(self, data=None, next=None, prev=None):
self.data = data
self.next = next
self.prev = prev
def disconnect(self):
self.data = None
self.next = None
self.prev = None
def __init__(self):
self.header = DoublyLinkedList.Node()
self.trailer = DoublyLinkedList.Node()
self.header.next = self.trailer
self.trailer.prev = self.header
self.size = 0
def __len__(self):
return self.size
def is_empty(self):
return (len(self) == 0)
def first_node(self):
if (self.is_empty()):
raise EmptyCollection("List is empty")
return self.header.next
def last_node(self):
if (self.is_empty()):
raise EmptyCollection("List is empty")
return self.trailer.prev
def add_first(self, elem):
return self.add_after(self.header, elem)
def add_last(self, elem):
return self.add_after(self.trailer.prev, elem)
def add_after(self, node, elem):
prev = node
succ = node.next
new_node = DoublyLinkedList.Node()
new_node.data = elem
new_node.prev = prev
new_node.next = succ
prev.next = new_node
succ.prev = new_node
self.size += 1
return new_node
def add_before(self, node, elem):
return self.add_after(node.prev, elem)
def delete(self, node):
prev = node.prev
succ = node.next
prev.next = succ
succ.prev = prev
self.size -= 1
data = node.data
node.disconnect()
return data
def __iter__(self):
if(self.is_empty()):
return
cursor = self.first_node()
while(cursor is not self.trailer):
yield cursor.data
cursor = cursor.next
def __str__(self):
return '[' + '<-->'.join([str(elem) for elem in self]) + ']'
def __repr__(self):
return str(self)
def merge_linked_lists(srt_lnk_lst1, srt_lnk_lst2):
return merge_sublists(srt_lnk_lst1.first_node(), srt_lnk_lst2.first_node())
def merge_sublists(a, b):
result = DoublyLinkedList()
if a is not None and b is not None:
if a.data < b.data:
result.add_last(a.data)
return merge_sublists(a.next, b)
else:
result.add_last(b.data)
return merge_sublists(a, b.next)
elif a is not None:
result.add_last(a.data)
return merge_sublists(a.next, b)
elif b is not None:
result.add_last(b.data)
return merge_sublists(a, b.next)
return result
ll1 = DoublyLinkedList()
for i in range(0,10,2):
ll1.add_last(i)
ll2 = DoublyLinkedList()
for i in range(1,10,2):
ll2.add_last(i)
merged = merge_linked_lists(ll1, ll2)
print(merged)
I get an error in my merge_sublists function that says I cannot add 'int' and 'Nonetype'.
Is there any way I can modify my merge_sublists method to make the function work properly? Any help is appreciated.
When you hit the end of the a list, although the object isn't None, you have a.data as None. You need to check the data as well as the list objects.
The problem is caused by the dummy list nodes that are placed at the start and end of the list. Although the code checks for empty lists, and skips over the initial dummy node, there is no check for the final dummy node at the end of the list. So while following a.next and b.next, it eventually encounters a dummy node, which has None as the value of data. The error occurs when it tries to compare None to the data value in another node.
You could check if a.next and b.next are None at the top of merge_sublists, but you will still need to make sure you only end up with a single dummy node at the end of the merged list.
It might be simplest to eliminate the dummy nodes altogether, and just check for an empty list when adding new nodes.
I have to reverse a doubly linked list(DLL) between two nodes. I've done this with singly linked lists(SLL), but I find it harder to do with DLL's? It might be the having to do it between two particular nodes.
Here is the code for my DLLNode & DLL. When I test this code it seems to do nothing to my DLL. Any tips on what I'm doing wrong??
EDIT: So I'm inputting a linked list 'a','b','c','d','e','f' and call twist('b','e'): This should result in the linked list 'a' 'b' 'e' 'd' 'c' 'f'
class DoublyLinkedListNode:
def __init__(self, item, prevnode = None, nextnode = None):
self._data = item
self.setnext(nextnode)
self.setprev(prevnode)
def data(self):
return self._data
def next(self):
return self._next
def prev(self):
return self._prev
def setprev(self, prevnode):
self._prev = prevnode
def setnext(self, nextnode):
self._next = nextnode
class DoublyLinkedList:
def __init__(self):
self._head = None
self._tail = None
self._length = 0
def _newnode(self, item, nextnode = None, prevnode = None):
return DoublyLinkedListNode(item, nextnode, prevnode)
def addfirst(self, item):
if self.isempty():
return self._addtoempty(item)
node = self._newnode(item, None, self._head)
self._head.setprev(node)
self._head = node
self._length += 1
def addlast(self, item):
if self.isempty():
return self._addtoempty(item)
node = self._newnode(item, self._tail, None)
self._tail.setnext(node)
self._tail = node
self._length += 1
def _addtoempty(self, item):
node = self._newnode(item, None, None)
self._head = self._tail = node
self._length = 1
def removefirst(self):
if len(self) <= 1:
return self._removelastitem()
data = self._head.data()
self._head = self._head.next()
self._head.setprev(None)
self._length -= 1
return data
def removelast(self):
if len(self) <= 1:
return self._removelastitem()
data = self._tail.data()
self._tail = self._tail.prev()
self._tail.setnext(None)
self._length -= 1
return data
def _removelastitem(self):
if self.isempty():
return None # Should probably raise an error.
data = self._head.data()
self._head = self._tail = None
self._length = 0
return data
def twist(self, endpt1, endpt2):
current = self._head
while current != None:
if current.data() == endpt1:
current.next().setnext(endpt2.next())
endpt2.setnext(current.next())
current.setnext(endpt2)
else:
current = current.next()
def isempty(self):
return len(self) == 0
def _nodes(self):
node = self._head
while node is not None:
yield node
node = node.next()
def __iter__(self):
for node in self._nodes():
yield node.data()
def __len__(self):
return self._length
def __str__(self):
items = [str(data) for data in self]
return ", ".join(items)
Here is the test I'm running:
def testtwist1(self):
n = [0,1,2,3,4,5]
L = DoublyLinkedList()
for i in n:
L.addlast(i)
L.twist(2,5)
print(L) # returns the list 0,1,2,3,4,5
I don't see how this executes at all. You've apparently set up a DLL, and then called DLL.twist('b', 'e'). Thus, endpt1 = 'b' and endpt2 = 'e'. You then compare endpt1 to current.data, but then you access endpt2.next. endpt2 is a single-character string.
You never reference the prev pointers at all.
The only time you try to alter anything is when you hit node b. Then you seem to try to exchange the next pointers of b and e (so b.next is f, and e.next is c, but that's all.
At this point, I expect that your forward links give you two lists: a->b->f and c->d->e->c (a cycle), and that the backward links are unchanged.
Get pencil and paper or a white board and walk through these changes, one statement at a time; that's what I do ...
I recovered twist from your previous post, fixed the indentation, and ran. As I explain above, this faults in execution:
Traceback (most recent call last):
File "so.py", line 113, in <module>
L.twist(2,5)
File "so.py", line 102, in twist
current.next().setnext(endpt2.next())
AttributeError: 'int' object has no attribute 'next'
There is no such thing as 2.next(). I don't think you're actually getting to your print statement. Also, print(L) will not print the node values in order; you haven't included a __repr__ method for your DLL object. __str__ would do the job, but you've use the list head pointer as if it were an iterable over the forward-linked list.
I strongly recommend that you back up and approach this with incremental programming: write a method or a few lines, test that, and don't continue until that works as you expect. Yes, this means that you're writing detailed tests as you go ... that's a good thing. Keep them, and keep running them.
I am trying to make a linked list queue in python and I cannot figure out how to return the size and the first item in the list...which seems pretty easy. I can insert and delete, but I cannot return the size or first item. Any thoughts??
class Node(object):
def __init__(self, item = None):
self.item = item
self.next = None
self.previous = None
class Queue(object):
def __init__(self):
"""post: creates an empty FIFO queue"""
self.length = 0
self.head = None
self.tail = None
def enqueue(self, x):
"""post: adds x at back of queue"""
newNode = Node(x)
newNode.next = None
if self.head == None:
self.head = newNode
self.tail = newNode
else:
self.tail.next = newNode
newNode.previous = self.tail
self.tail = newNode
def dequeue (self):
"""pre: self.size() > 0
post: removes and returns the front item"""
item = self.head.item
self.head = self.head.next
self.length = self.length - 1
if self.length == 0:
self.last = None
return item
def front(self):
"""pre: self.size() > 0
post: returns first item in queue"""
return item[0]
def size(self):
"""post: returns the number of itemes in queue"""
To efficiently be able to report the length of the linked list, you need to incriment it each time you add an element and decrement it each time you remove one. You're already doing the latter, but not the former.
So, just add self.length += 1 somewhere in your enqueue method, then size() can simple be return self.length
As for the first element in your queue, it will always be the item in the head node. So front() can be return self.head.item
Python lists already do what you're describing. Some examples:
# create a list
l = ['foo', 'bar']
# get the first item
print(l.pop(0))
# add an item
l.append(42)
print(l)
# get the size
print(len(l))
Your code in those two methods doesn't make any sense. How are you indexing into item? It's just a field of the Node class, not an array. Why didn't front() immediately lead you to thinking about head?
Surprisingly enough, the rest of your code seems okay. Here's what you need:
def front(self):
return self.head.item
def size(self):
return self.length
Also, you're not incrementing self.length in your enqueue() method.
The fact that you are having trouble with these should be a useful clue to you that you don't really understand the rest of the code. I've seen beginners often get mired in this trial-and-error approach, where you muck around with something until it works, usually starting with some code you got from somewhere. This leads to painfully brittle code, because your understanding is also brittle. This is not the way to write sensible code. At best it's a starting point for building your understanding - in which case, mucking around is exactly the right thing to do. Learn by experimentation and all that.
I recommend you read through the code you posted carefully and build a reasonably complete mental model of how it operates. Draw pictures or whatever helps you understand the pieces and the processes they implement. The depth of your mental model is a critical component of programming skill.
Also, you don't really need to go to all the trouble of writing these classes, other than as an exercise or something. Python lists already have methods that enable them to be used as queues.
First thing that jumps out at me is when you enqueue you need to increment the length of the list. size() should just need to return the length of the list once you've done that.
And then to access the first item of the list you appear to be trying to use list syntax which your list does not support (at least in the code I can see). Instead return self.head
class Node:
def init(self, data):
self.data = data
self.next = None
class Queue:
def __init__(self):
self.front = None
self.rear = None
self.size = 0
def enQueue(self, data):
temp = Node(data)
if self.front == None:
self.front = self.rear = temp
self.size += 1 # get track of size
return
self.rear.next = temp
self.rear = temp
self.size += 1
def deQueue(self):
if self.front == None:
return
temp = self.front
self.front = self.front.next
if self.front == None:
self.rear = None
del temp
self.size -= 1
def isEmpty(self):
return self.front == None
def getSize(self):
return self.size
def getFront(self):
if self.size > 0:
return self.front.data
else:
return
def getRear(self):
if self.size > 0:
return self.rear.data
else:
return
def printQueue(self):
queue = []
curr = self.front
while curr != None:
queue.append(curr.data)
curr = curr.next
print(queue)
_NodeLinked class is to create nodes:
class _NodeLinked:
# slots is memory efficient way to store the instance attributes
__slots__ = '_element', '_next'
def __init__(self, element, next):
self._element = element
self._next = next
class QueuesLinked:
# each instance being initialized an empty list
def __init__(self):
self._front = None
self._rear = None
self._size = 0
def __len__(self):
return self._size
def isempty(self):
return self._size == 0
# adding a node to the end of the list
def enqueue(self, e):
newest = _NodeLinked(e, None)
if self.isempty():
self._front = newest
else:
self._rear._next = newest
self._rear = newest
self._size += 1
# removing first node
def dequeue(self):
if self.isempty():
print('Queue is empty')
return
e = self._front._element
self._front = self._front._next
self._size -= 1
if self.isempty():
self._rear = None
return e
def first(self):
if self.isempty():
print('Queue is empty')
return
return self._front._element
def display(self):
p = self._front
while p:
print(p._element,end=' <-- ')
p = p._next
print()