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) +```