March 11, 2024
LinkedList
LinkedList
contains an instance of ListNode
in _head
addfirst
creates a new ListNode
and updates _head
removefirst
updates _head
and returns the dataQueue
with a LinkedList
class LinkedList:
def __init__(self):
self._head = None
def addfirst(self, item):
self._head = ListNode(item, self._head)
def addlast(self, item):
if self._head is None:
self.addfirst(item)
else:
currentnode = self._head
while currentnode.link is not None:
currentnode = currentnode.link
currentnode.link = ListNode(item)
def removefirst(self):
item = self._head.data
self._head = self._head.link
return item
def removelast(self):
if self._head.link is None:
return self.removefirst()
else:
currentnode = self._head
while currentnode.link.link is not None:
currentnode = currentnode.link
item = currentnode.link.data
currentnode.link = None
return item
LinkedList
LL = LinkedList()
LL.addfirst(3)
LL.addfirst(5)
print(LL.removefirst() == 5)
LL.addlast(9)
LL.addlast(13)
print(LL.removefirst() == 3)
print(LL.removefirst() == 9)
print(LL.removelast() == 13)
True
True
True
True
LinkedList
can be used to build a Queue
while
loops!ListQueue
LinkedList
?def addlast(self, item):
if self._head is None:
self.addfirst(item)
else:
currentnode = self._head
while currentnode.link is not None:
currentnode = currentnode.link
currentnode.link = ListNode(item)
while currentnode.link is not None
is not efficientdef removelast(self):
if self._head.link is None:
return self.removefirst()
else:
currentnode = self._head
while currentnode.link.link is not None:
currentnode = currentnode.link
item = currentnode.link.data
currentnode.link = None
return item
while currentnode.link.link is not None
not efficientremovelast
traverses all nodes to find second-to-last oneQueue
with a LinkedList
class LinkedListNew:
def __init__(self):
self._head = None
self._tail = None
def addfirst(self, item):
self._head = ListNode(item, self._head)
if self._tail is None: self._tail = self._head
def addlast(self, item):
if self._head is None:
self.addfirst(item)
else:
self._tail.link = ListNode(item)
self._tail = self._tail.link
def removefirst(self):
item = self._head.data
self._head = self._head.link
if self._head is None: self._tail = None
return item
def removelast(self):
if self._head is self._tail:
return self.removefirst()
else:
currentnode = self._head
while currentnode.link is not self._tail:
currentnode = currentnode.link
item = self._tail.data
self._tail = currentnode
self._tail.link = None
return item
LinkedList
LL = LinkedListNew()
LL.addfirst(3)
LL.addfirst(5)
print(LL.removefirst() == 5)
LL.addlast(9)
LL.addlast(13)
print(LL.removefirst() == 3)
print(LL.removefirst() == 9)
print(LL.removelast() == 13)
True
True
True
True
LinkedList
can be used to build a Queue
while
loop!ListQueue
LinkedList
?def addlast(self, item):
if self._head is None:
self.addfirst(item)
else:
self._tail.link = ListNode(item)
self._tail = self._tail.link
while
loop in addlast
addfirst
is calledself._tail
is updated and new node is addeddef removelast(self):
if self._head is self._tail:
return self.removefirst()
else:
currentnode = self._head
while currentnode.link is not self._tail:
currentnode = currentnode.link
item = self._tail.data
self._tail = currentnode
self._tail.link = None
return item
while currentnode.link is not self._tail
not efficientremovelast
still traverses nodes to perform a removal!LinkedList
class LinkedListPrime:
def __init__(self):
self._head = None
self._tail = None
self._length = 0
def addfirst(self, item):
self._head = ListNode(item, self._head)
if self._tail is None: self._tail = self._head
self._length += 1
def addlast(self, item):
if self._head is None:
self.addfirst(item)
else:
self._tail.link = ListNode(item)
self._tail = self._tail.link
self._length += 1
def removefirst(self):
item = self._head.data
self._head = self._head.link
if self._head is None: self._tail = None
self._length -= 1
return item
def removelast(self):
if self._head is self._tail:
return self.removefirst()
else:
currentnode = self._head
while currentnode.link is not self._tail:
currentnode = currentnode.link
item = self._tail.data
self._tail = currentnode
self._tail.link = None
self._length -= 1
return item
def __len__(self):
return self._length
__init__
self._length = 0
is a \(O(1)\) operationaddfirst
and addlast
self._length += 1
is a \(O(1)\) operationremovefirst
and removelast
self._length -= 1
is a \(O(1)\) operationLinkedList
LL = LinkedListPrime()
LL.addfirst(3)
LL.addfirst(5)
print(LL.removefirst() == 5)
LL.addlast(9)
LL.addlast(13)
print(LL.removefirst() == 3)
print(LL.removefirst() == 9)
print(LL.removelast() == 13)
print(len(LL) == 0)
True
True
True
True
True
LinkedListPrime
has features equivalent to LinkedListNew
Queue
LinkedQueue
with a LinkedList
class LinkedQueue:
def __init__(self):
self._L = LinkedListPrime()
def enqueue(self, item):
self._L.addlast(item)
def dequeue(self):
return self._L.removefirst()
def peek(self):
item = self._L.removefirst()
self._L.addfirst(item)
return item
def display(self):
current = self._L._head
while current is not None:
print(current.data, end=" ")
current = current.link
def __len__(self):
return len(self._L)
def isempty(self):
return len(self) == 0
LinkedQueue
def manipulate_queue():
queue = LinkedQueue()
queue.enqueue('umbrella')
queue.enqueue('backpack')
queue.enqueue('sandals')
print("Queue contents after enqueue operations:", end=" ")
queue.display()
queue.dequeue()
print("\nQueue contents after dequeue operation:", end=" ")
queue.display()
manipulate_queue()
Queue contents after enqueue operations: umbrella backpack sandals
Queue contents after dequeue operation: backpack sandals
LinkedQueue
is functionally equivalent to the ListQueue
LinkedQueue
uses display
to reveal the contentsListNode
self.data
stores the data value inside of the ListNode
self.prev
stores a reference to the previous ListNode
self.link
stores a reference to the next ListNode
ListNode
: for any two nodes a
and b
, it must be true that b == a.link
if and only if a = b.prev
DoublyLinkedList
class DoublyLinkedList:
def __init__(self):
self._head = None
self._tail = None
self._length = 0
def addfirst(self, item):
if len(self) == 0:
self._head = self._tail = ListNode(item, None, None)
else:
newnode = ListNode(item, None, self._head)
self._head.prev = newnode
self._head = newnode
self._length += 1
def addlast(self, item):
if len(self) == 0:
self._head = self._tail = ListNode(item, None, None)
else:
newnode = ListNode(item, self._tail, None)
self._tail.link = newnode
self._tail = newnode
self._length += 1
def __len__(self):
return self._length
addfirst
and addlast
!def _addbetween(self, item, before, after):
node = ListNode(item, before, after)
if after is self._head:
self._head = node
if before is self._tail:
self._tail = node
self._length += 1
_addbetween
addfirst
and addlast
methods call _addbetween
DoublyLinkedList
class DoublyLinkedList:
def __init__(self):
self._head = None
self._tail = None
self._length = 0
def __len__(self):
return self._length
def _addbetween(self, item, before, after):
node = ListNode(item, before, after)
if after is self._head:
self._head = node
if before is self._tail:
self._tail = node
self._length += 1
def addfirst(self, item):
self._addbetween(item, None, self._head)
def addlast(self, item):
self._addbetween(item, self._tail, None)
def _remove(self, node):
before, after = node.prev, node.link
if node is self._head:
self._head = after
else:
before.link = after
if node is self._tail:
self._tail = before
else:
after.prev = before
self._length -= 1
return node.data
def removefirst(self):
return self._remove(self._head)
def removelast(self):
return self._remove(self._tail)
def display(self):
current = self._head
while current is not None:
print(current.data)
current = current.next
DoublyLinkedList
def manipulate_doubly_linked_list():
DLL = DoublyLinkedList()
print(len(DLL))
DLL.addfirst(3)
DLL.addfirst(5)
print(DLL.removefirst() == 5)
DLL.addlast(9)
DLL.addlast(13)
print(DLL.removefirst() == 3)
print(DLL.removefirst() == 9)
print(DLL.removefirst() == 13)
print(len(DLL))
manipulate_doubly_linked_list()
0
True
True
True
True
0
List
or DoublyLinkedList
?C = A + B
concatenates the two lists together+
creates a new listC
without modifying A
or B
DoublyLinkedList
permitted, efficient concatenation!def __iadd__(self, other):
if other._head is not None:
if self._head is None:
self._head = other._head
else:
self._tail.link = other._head
other._head.prev = self._tail
self._tail = other._tail
self._length = self._length + other._length
other.__init__()
return self
__iadd__
method is called when +=
is usedother
DoublyLinkedList
is concatenated to self
DoublyLinkedList
L = DoublyLinkedList()
[L.addlast(i) for i in range(11)]
B = DoublyLinkedList()
[B.addlast(i+11) for i in range(10)]
L += B
n = L._head
while n is not None:
print(n.data, end = ' ')
n = n.link
DoublyLinkedList
L
is concatenated with B
+=
calls the __iadd__
method in DoublyLinkedList
B
list is drained of values when it is the other
parameterDeque
with a DoublyLinkedList
?Deque
, or a double-ended queue, will contain an instance of the DoublyLinkedList
as was done previouslyDeque
endsDeque
are \(O(1)\) operations!Algorithmology