Stack and Queue
Stack
Stack is a linear list limited by the rule FILO (First In Last Out).
We can use array or linked list to implement this data structure.
class ArrayStack:
def __init__(self, n):
self.items = []
self.n = n
self.count = 0
def push(item) -> bool:
'''
Add the item to the end of the array
O(1)
'''
if self.count == self.n:
return False
self.items.append(item)
self.count += 1
return True
def pop(item):
'''
return the last item of the array
O(1)
'''
if self.count == 0:
return None
self.count -= 1
return self.item[self.count]
class LinkedNode:
def __init__(self, val):
self.val = val
self.next = None
class LinkedListStack:
def __init__(self, n):
self.head = LinkedNode(None)
self.n = n
self.count = 0
def push(self, item) -> bool:
'''
Add item to the first node in the linked list
O(1)
'''
if self.count == self.n:
return False
new_node = LinkedNode(item)
new_node.next = self.head.next
self.head.next = new_node
self.count += 1
return True
def pop(self):
'''
return the val of the first node in the linked list
O(1)
'''
if self.head.next is None:
return None
tmp = self.head.next
self.head.next = tmp.next
tmp.next = None
return tmp.val
Problems:
-
There are 2 ways to use stack in this problem.
-
use two stacks, one for numbers, one for operations.
use two loops. The first loop reads digitals and deals with the
*
and/
operations. The second one deals with the rest and returns the result -
use one stack for number. The number can be positive and negative
use 2 global variables, previous number and previous operator.
use one loop, we always deal with the previous case and update these two variable.
DO NOT forget the last char in the string.
-
Queue
Queue is a limited linear list, too. The rule is FIFO (First In First Out).
We can use array or linked list to implement this data structure.
class ArrayQueue:
def __init__(self, n):
self.items = []
self.n = n
self.head = 0 # index of the first item
self.tail = 0 # index after the last item
def enqueue(self, item) -> bool:
if self.tail == self.n:
if self.head == 0:
# Queue is full.
return False
for i in range(self.head, self.n):
self.items[i - self.head] = self.items[i]
self.tail = self.n - self.head
self.head = 0
self.items[self.tail] = item
self.tail += 1
return True
def dequeue(self):
if self.head == self.tail:
return None
item = self.items[self.head]
self.head += 1
return item
class LinkedNode:
def __init__(self, val):
self.val = val
self.next = None
class LinkedListQueue:
def __init__(self, n):
self.head = LinkedNode(None) # node before the first node
self.tail = self.head # node of the last node
self.n = n
self.count = 0
def enqueue(self, item):
if self.count == self.n:
return False
new_node = LinkedNode(item)
self.tail.next = new_node
self.tail = self.tail.next
self.count += 1
return True
def dequeue(self):
if self.count == 0:
return None
val = self.head.next.val
self.head.next = self.head.next.next
self.count -= 1
return val
Circular Queue
The circular queue is based on array queue. The tail of the queue is followed by the head.
It doesn't need to move data when the queue is "fake" full (tail == n && head != 0
).
In practice, There is an empty space between the head and the tail. It helps to define the conditions of full queue and empty queue.
Two edge cases are important to a queue.
-
The queue is full.
-
The queue is empty.
For a normal array queue, these 2 are:
-
Full:
tail == n
-
Empty:
head == tail
For the circular array queue, these 2 are:
-
Full:
(tail + 1) % n == head
We use
(tail + 1) % n
to movetail
to the next position. It's the same forhead
.(a + 1) % n
solves the special case. Whena == n - 1
, the next position ofa
should be0
. -
Empty:
head == tail
class CircularArrayQueue:
def __init__(self, n):
self.items = []
self.n = n
self.head = 0 # index of the first item
self.tail = 0 # index after the last item
def enqueue(self, item):
if (self.tail + 1) % self.n == self.head:
return False
self.items[self.tail] = item
self.tail = (self.tail + 1) % self.n
reture True
def dequeue(self):
if self.tail == self.head:
return None
val = self.items[self.head]
self.head = (self.head + 1) % n
The circular queue is widely used in pools, such as connection pool of database, message queue, concurrency, etc. It controls the distribution of the resource.