diff --git a/contrib/ds-algorithms/deque.md b/contrib/ds-algorithms/deque.md index 459ac04..db489a8 100644 --- a/contrib/ds-algorithms/deque.md +++ b/contrib/ds-algorithms/deque.md @@ -1,108 +1,168 @@ -## Double-Ended Queue (Deque) +# Deque in Python -A double-ended queue (deque) supports insertion and deletion from both ends, making it a more versatile queue implementation. +## Definition +A deque, short for double-ended queue, is an ordered collection of items that allows rapid insertion and deletion at both ends. -### Input-Restricted Deque +## Syntax +In Python, deques are implemented in the collections module: -In an input-restricted deque, elements can only be inserted from one end while deletions can occur from both ends. - -### Output-Restricted Deque - -An output-restricted deque allows elements to be deleted from one end only, while insertions can be made from both ends. - -## Real Life Examples of Deques - -### Task Scheduling - -Deques are useful in task scheduling algorithms where tasks can be added to either end and processed accordingly based on priority or other scheduling criteria. - -### Sliding Window Problems - -Algorithms solving sliding window problems often use deques to efficiently manage and query elements in the current window. - -### Implementations in Python - -Python provides a built-in `collections.deque` which supports efficient append and pop operations from both ends. It's ideal for scenarios requiring a simple and efficient double-ended queue. - -## Operations on a Deque - -- **isEmpty**: Checks if the deque is empty. -- **appendLeft**: Adds an element to the left end of the deque. -- **appendRight**: Adds an element to the right end of the deque. -- **popLeft**: Removes and returns the element from the left end of the deque. -- **popRight**: Removes and returns the element from the right end of the deque. -- **peekLeft**: Returns the element from the left end without removing it. -- **peekRight**: Returns the element from the right end without removing it. -- **clear**: Removes all elements from the deque. - -## Implementation of Deque in Python - -Python's `collections.deque` provides an efficient implementation of a deque. - -```python +```py from collections import deque # Creating a deque -dq = deque() - -# Adding elements to the deque -dq.append(1) -dq.append(2) -dq.append(3) - -# Removing elements from the deque -dq.popleft() # Removes and returns 1 -dq.pop() # Removes and returns 3 - -# Peeking elements -print("Left end peek:", dq[0]) -print("Right end peek:", dq[-1]) - -# Displaying elements in the deque -print("Deque:", list(dq)) +d = deque(iterable) # Create deque from iterable (optional) ``` -## Example: Sliding Window Maximum +## Operations +1. **Appending Elements**: -In this example, we'll use a deque to efficiently find the maximum element in sliding windows of a list. + - append(x): Adds element x to the right end of the deque. + - appendleft(x): Adds element x to the left end of the deque. -```python +2. **Removing Elements**: + + - pop(): Removes and returns the rightmost element. + - popleft(): Removes and returns the leftmost element. + +3. **Accessing Elements**: + + - deque[index]: Accesses element at index. + +4. **Other Operations**: + + - extend(iterable): Extends deque by appending elements from iterable. + - extendleft(iterable): Extends deque by appending elements from iterable to the left. + - rotate(n): Rotates deque n steps to the right (negative n rotates left). + +## Example + +### 1. showing all the operations +```py from collections import deque -def sliding_window_maximum(nums, k): +# Initialize a deque +d = deque([1, 2, 3, 4, 5]) +print("Initial deque:", d) + +# Append elements +d.append(6) +print("After append(6):", d) + +# Append left +d.appendleft(0) +print("After appendleft(0):", d) + +# Pop from the right end +rightmost = d.pop() +print("Popped from right end:", rightmost) +print("Deque after pop():", d) + +# Pop from the left end +leftmost = d.popleft() +print("Popped from left end:", leftmost) +print("Deque after popleft():", d) + +# Accessing elements +print("Element at index 2:", d[2]) + +# Extend deque +d.extend([6, 7, 8]) +print("After extend([6, 7, 8]):", d) + +# Extend left +d.extendleft([-1, 0]) +print("After extendleft([-1, 0]):", d) + +# Rotate deque +d.rotate(2) +print("After rotate(2):", d) + +# Rotate left +d.rotate(-3) +print("After rotate(-3):", d) +``` +Output +```py +Initial deque: deque([1, 2, 3, 4, 5]) +After append(6): deque([1, 2, 3, 4, 5, 6]) +After appendleft(0): deque([0, 1, 2, 3, 4, 5, 6]) +Popped from right end: 6 +Deque after pop(): deque([0, 1, 2, 3, 4, 5]) +Popped from left end: 0 +Deque after popleft(): deque([1, 2, 3, 4, 5]) +Element at index 2: 3 +After extend([6, 7, 8]): deque([1, 2, 3, 4, 5, 6, 7, 8]) +After extendleft([-1, 0]): deque([0, -1, 1, 2, 3, 4, 5, 6, 7, 8]) +After rotate(2): deque([7, 8, 0, -1, 1, 2, 3, 4, 5, 6]) +After rotate(-3): deque([1, 2, 3, 4, 5, 6, 7, 8, 0, -1]) + +``` + +### 2. Finding Maximum in Sliding Window +```py +from collections import deque + +def max_sliding_window(nums, k): if not nums: return [] - - n = len(nums) + + d = deque() result = [] - dq = deque() - - for i in range(n): - # Remove elements from the deque that are out of the current window - while dq and dq[0] <= i - k: - dq.popleft() - - # Remove elements from the deque that are less than the current element - while dq and nums[dq[-1]] <= nums[i]: - dq.pop() - - # Add the current element index to the deque - dq.append(i) - - # Add the maximum of the current window to the result + + for i, num in enumerate(nums): + # Remove elements from deque that are out of the current window + if d and d[0] <= i - k: + d.popleft() + + # Remove elements from deque smaller than the current element + while d and nums[d[-1]] <= num: + d.pop() + + d.append(i) + + # Add maximum for current window if i >= k - 1: - result.append(nums[dq[0]]) - + result.append(nums[d[0]]) + return result # Example usage: nums = [1, 3, -1, -3, 5, 3, 6, 7] k = 3 -print("Sliding Window Maximum:", sliding_window_maximum(nums, k)) +print("Maximums in sliding window of size", k, "are:", max_sliding_window(nums, k)) + ``` -## Conclusion +Output +```py +Maximums in sliding window of size 3 are: [3, 3, 5, 5, 6, 7] +``` -Queues and deques are fundamental data structures that facilitate efficient data processing and -management in various applications. Understanding their principles and implementations is crucial -for developing robust software solutions. \ No newline at end of file + +## Applications +- **Efficient Queues and Stacks**: Deques allow fast O(1) append and pop operations from both ends, +making them ideal for implementing queues and stacks. +- **Sliding Window Maximum/Minimum**: Used in algorithms that require efficient windowed +computations. + + +## Advantages +- Efficiency: O(1) time complexity for append and pop operations from both ends. +- Versatility: Can function both as a queue and as a stack. +- Flexible: Supports rotation and slicing operations efficiently. + + +## Disadvantages +- Memory Usage: Requires more memory compared to simple lists due to overhead in managing linked +nodes. + +## Conclusion +- Deques in Python, provided by the collections.deque module, offer efficient double-ended queue +operations with O(1) time complexity for append and pop operations on both ends. They are versatile +data structures suitable for implementing queues, stacks, and more complex algorithms requiring +efficient manipulation of elements at both ends. + +- While deques excel in scenarios requiring fast append and pop operations from either end, they do +consume more memory compared to simple lists due to their implementation using doubly-linked lists. +However, their flexibility and efficiency make them invaluable for various programming tasks and +algorithmic solutions. \ No newline at end of file