Stacks, Queues & Heaps
© Joe James, 2019.
Stack using Python List
Stack is a LIFO data structure – last-in, first-out.
Use append() to push an item onto the stack.
Use pop() to remove an item.
my_stack = list()
my_stack.append(4)
my_stack.append(7)
my_stack.append(12)
my_stack.append(19)
print(my_stack)
[4, 7, 12, 19]
print(my_stack.pop())
print(my_stack.pop())
print(my_stack)
19
12
[4, 7]
Stack using List with a Wrapper Class
We create a Stack class and a full set of Stack methods.
But the underlying data structure is really a Python List.
For pop and peek methods we first check whether the stack is empty, to avoid exceptions.
class Stack():
def __init__(self):
self.stack = list()
def push(self, item):
self.stack.append(item)
def pop(self):
if len(self.stack) > 0:
return self.stack.pop()
else:
return None
def peek(self):
if len(self.stack) > 0:
return self.stack[len(self.stack)-1]
else:
return None
def __str__(self):
return str(self.stack)
Test Code for Stack Wrapper Class
my_stack = stack()
my_stack.push(1)
my_stack.push(3)
print(my_stack)
print(my_stack.pop())
print(my_stack.peek())
print(my_stack.pop())
print(my_stack.pop())
[1, 3]
3
1
1
None
Queue using Python Deque
Queue is a FIFO data structure – first-in, first-out.
Deque is a double-ended queue, but we can use it for our queue.
We use append() to enqueue an item, and popleft() to dequeue an item.
See Python docs for deque.
from collections import deque
my_queue = deque()
my_queue.append(5)
my_queue.append(10)
print(my_queue)
print(my_queue.popleft())
deque([5, 10])
5
Fun exercise:
Write a wrapper class for the Queue class, similar to what we did for Stack, but using Python deque.
Try adding enqueue, dequeue, and get_size methods.
Python Single-ended Queue Wrapper Class using Deque
We rename the append method to enqueue, and popleft to dequeue.
We also add peek and get_size operations.
from collections import deque
class Queue():
def __init__(self):
self.queue = deque()
self.size = 0
def enqueue(self, item):
self.queue.append(item)
self.size += 1
def dequeue(self):
if self.size > 0:
self.size -= 1
return self.queue.popleft()
else:
return None
def peek(self):
if self.size > 0:
ret_val = self.queue.popleft()
self.queue.appendleft(ret_val)
return ret_val
else:
return None
def get_size(self):
return self.size
def __str__(self):
return str(self.queue)
__repr__ = __str__
test_que = Queue()
test_que.enqueue(1)
test_que.enqueue(2)
test_que.enqueue(3)
test_que.enqueue(4)
print(test_que)
test_que.dequeue()
print(test_que)
print(test_que.peek())
deque([1, 2, 3, 4])
deque([2, 3, 4])
2
Python MaxHeap
A MaxHeap always bubbles the highest value to the top, so it can be removed instantly.
Public functions: push, peek, pop
Private functions: __swap, __floatUp, __bubbleDown, str.
MaxHeap is fast!
- Insert in O(log n)
- Get Max in O(1)
- Remove Max in O(log n)
class MaxHeap:
def __init__(self, items=[]):
super().__init__()
self.heap = [0]
for item in items:
self.heap.append(item)
self.__floatUp(len(self.heap) - 1)
def push(self, data):
self.heap.append(data)
self.__floatUp(len(self.heap) - 1)
def peek(self):
if self.heap[1]:
return self.heap[1]
else:
return False
def pop(self):
if len(self.heap) > 2:
self.__swap(1, len(self.heap) - 1)
max = self.heap.pop()
self.__bubbleDown(1)
elif len(self.heap) == 2:
max = self.heap.pop()
else:
max = False
return max
def __swap(self, i, j):
self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
def __floatUp(self, index):
parent = index//2
if index <= 1:
return
elif self.heap[index] > self.heap[parent]:
self.__swap(index, parent)
self.__floatUp(parent)
def __bubbleDown(self, index):
left = index * 2
right = index * 2 + 1
largest = index
if len(self.heap) > left and self.heap[largest] < self.heap[left]:
largest = left
if len(self.heap) > right and self.heap[largest] < self.heap[right]:
largest = right
if largest != index:
self.__swap(index, largest)
self.__bubbleDown(largest)
def __str__(self):
return str(self.heap)
MaxHeap Test Code
m = MaxHeap([95, 3, 21])
m.push(10)
print(m)
print(m.pop())
print(m.peek())
[0, 95, 10, 21, 3]
95
21