From 5f44f6b89f1091fc16409d156d59f63c1093321f Mon Sep 17 00:00:00 2001
From: marina <138340846+bt3gl-cryptographer@users.noreply.github.com>
Date: Mon, 7 Aug 2023 19:37:37 -0700
Subject: [PATCH] Update README.md
---
queues/README.md | 168 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 134 insertions(+), 34 deletions(-)
diff --git a/queues/README.md b/queues/README.md
index 8a154fd..5b5d97f 100644
--- a/queues/README.md
+++ b/queues/README.md
@@ -4,7 +4,7 @@
* queues are **first in, first out (FIFO) abstract structures** (*i.e.*, items are removed at the same order they are added) that can be implemented with two arrays or a dynamic array (linked list), as long as items are added and removed from opposite sides.
-* queues support **enqueue** (add to the end one end) and **dequeue** (remove from the other end, or tail).
+* queues support **enqueue** (add to one end) and **dequeue** (remove from the other end, or tail).
* if implemented with a dynamic array, a more efficient solution is to use a circular queue (ring buffer), *i.e.*, a fixed-size array and two pointers to indicate the starting and ending positions.
* an advantage of circular queues is that we can use the spaces in front of the queue.
@@ -13,6 +13,7 @@
* queues are often used in **breath-first search** (where you store a list of nodes to be processed) or when implementing a cache.
+
---
@@ -25,18 +26,51 @@
-#### using arrays
+#### with an array
* to build a ring with a fixed size array, any of the elements could be considered as the head.
+
+* to enqueue, you loop the queue with the tail index until find a `None` (even if it has to loop back):
+
+
+
+```python
+while self.queue[self.tail] is not None:
+ self.tail += 1
+ if self.tail == self.size:
+ self.tail = 0
+self.queue[self.tail] = value
+```
+
+
+
+* to dequeue, you simply pop the head value:
+
+
+
+```python
+value = self.queue[self.head]
+self.queue[self.head] = None
+self.head += 1
+```
+
+
+
+* there is one occasion when `tail` index is set to zero:
+ * when the enqueue operation adds to the last position in the array and tail has to loop back (`self.tail == self.size`).
+
+* there are two occasions when `head` index is set to zero:
+ * when the queue is checked as empty
+ * when the dequeue operation popped the last element in the array and head has to loop back (`self.head == self.size`).
-* as long as we know the length of the queue, we can instantly locat its tails based on this formula:
+* as long as we know the length of the queue, we can instantly locate its tails based on this formula:
```
-tail_index = (head_index + queue_length - 1) % queue_capacity
+tail_index = (head_index + current_length - 1) % size
```
@@ -45,33 +79,28 @@ tail_index = (head_index + queue_length - 1) % queue_capacity
```python
class CircularQueue:
- def __init__(self, k: int):
+ def __init__(self, size):
self.head = 0
self.tail = 0
- self.size = k
+ self.size = size
self.queue = [None] * self.size
def enqueue(self, value: int) -> bool:
-
- if value is None:
- return False
-
if self.is_full():
return False
if self.is_empty():
- self.heard = 0
+ self.head = 0
while self.queue[self.tail] is not None:
self.tail += 1
if self.tail == self.size:
self.tail = 0
-
self.queue[self.tail] = value
+
return True
def dequeue(self) -> bool:
-
if self.is_empty():
return False
@@ -85,10 +114,10 @@ class CircularQueue:
return True
def front(self) -> int:
- return self.queue[self.head] or -1
+ return self.queue[self.head] or False
def rear(self) -> int:
- return self.queue[self.tail] or -1
+ return self.queue[self.tail] or False
def is_empty(self) -> bool:
for n in self.queue:
@@ -105,10 +134,14 @@ class CircularQueue:
-#### using linked lists
+
+
+### with linked lists
+* in this example we implement the methods `is_empty` and `is_full` using an extra counter variable `self.count` that can be compared to `self.size` or `0` and used to validate `rear` and `front`.
+
* note that this queue is not thread-safe: the data structure could be corrupted in a multi-threaded environment (as race-condition could occur). to mitigate this problem, one could add the protection of a lock.
@@ -116,57 +149,124 @@ class CircularQueue:
```python
class Node:
def __init__(self, value, next=None):
+
self.value = value
self.next = next
-class CircularQueue:
+class Queue:
- def __init__(self, k: int):
- self.capacity = k
+ def __init__(self, size):
+
+ self.size = size
self.count = 0
self.head = None
self.tail = None
def enqueue(self, value: int) -> bool:
- if self.count == self.capacity:
+
+ if self.is_full():
return False
- if self.count == 0:
- self.head = Node(value)
- self.tail = self.head
+
+ if self.is_empty():
+ self.head = self.tail = Node(value)
+
else:
- new_node = Node(value)
- self.tail.next = new_node
- self.tail = new_node
+ self.tail.next = Node(value)
+ self.tail = self.tail.next
+
self.count += 1
+
return True
def dequeue(self) -> bool:
- if self.count == 0:
+
+ if self.is_empty():
return False
+
self.head = self.head.next
self.count -= 1
+
return True
def front(self) -> int:
- if self.count == 0:
- return -1
-
+ if self.is_empty():
+ return False
return self.head.value
def rear(self) -> int:
- if self.count == 0:
- return -1
+ if self.is_empty():
+ return False
return self.tail.value
def is_empty(self) -> bool:
return self.count == 0
def is_full(self) -> bool:
- return self.count == self.capacity
+ return self.count == self.size
```
-
+---
+### a stream with rate limiter
+
+
+
+```python
+class Logger:
+
+ def __init__(self):
+ self.msg_set = set()
+ self.msg_queue = deque()
+
+ def print_message(self, timestamp, message) -> bool:
+
+ while self.msg_queue:
+ msg, msg_timestamp = self.msg_queue[0]
+ if timestamp - msg_timestamp >= 10:
+ self.msg_queue.popleft()
+ self.msg_set.remove(msg)
+ else:
+ break
+
+ if message not in self.msg_set:
+ self.msg_set.add(message)
+ self.msg_queue.append((message, timestamp))
+ return True
+
+ return False
+```
+
+
+
+---
+
+### moving average
+
+
+
+* given a stream of integers and a window size, the moving average in the sliding window can be calculated with a queue:
+
+
+
+```python
+
+class MovingAverage:
+
+ def __init__(self, size: int):
+
+ self.queue = []
+ self.size = size
+
+
+ def next(self, val: int) -> float:
+
+ self.queue.append(val)
+
+ if len(self.queue) > self.size:
+ self.queue.pop(0)
+
+ return sum(self.queue) / len(self.queue)
+```