A Deque (double-ended queue) is a data structure that allows insertion and deletion at both ends. The UCX DSA package provides two deque implementations:
Deque: Fixed-capacity static deque
DynamicDeque: Automatically resizing deque
Both implementations use circular indexing internally for efficient space utilization.
deque = Deque()deque.append_left(1)deque.append_left(2)deque.append_left(3)print(deque.to_list()) # [3, 2, 1]# push_front is a synonym for append_leftdeque.push_front(4)print(deque.to_list()) # [4, 3, 2, 1]# Raises Exception if deque is fullfull_deque = Deque(capacity=2)full_deque.append_left(1)full_deque.append_left(2)try: full_deque.append_left(3)except Exception as e: print(e) # Deque Full
Time Complexity: O(1)
Remove and return the front element:
deque = Deque.from_list([1, 2, 3, 4, 5])front = deque.pop_left()print(front) # 1print(deque.to_list()) # [2, 3, 4, 5]# pop_front is a synonym for pop_leftfront = deque.pop_front()print(front) # 2# Raises Exception if deque is emptyempty_deque = Deque()try: empty_deque.pop_left()except Exception as e: print(e) # Empty Deque
Time Complexity: O(1)
View the front element without removing it:
deque = Deque.from_list([10, 20, 30])print(deque.peek_left()) # 10print(len(deque)) # 3 (unchanged)# front is a synonym for peek_leftprint(deque.front()) # 10# Raises Exception if deque is emptyempty_deque = Deque()try: empty_deque.peek_left()except Exception as e: print(e) # Empty deque
deque = Deque()deque.append_right(1)deque.append_right(2)deque.append_right(3)print(deque.to_list()) # [1, 2, 3]# push_back is a synonym for append_rightdeque.push_back(4)print(deque.to_list()) # [1, 2, 3, 4]# Raises Exception if deque is fullfull_deque = Deque(capacity=2)full_deque.append_right(1)full_deque.append_right(2)try: full_deque.append_right(3)except Exception as e: print(e) # Deque Full
Time Complexity: O(1)
Remove and return the back element:
deque = Deque.from_list([1, 2, 3, 4, 5])back = deque.pop_right()print(back) # 5print(deque.to_list()) # [1, 2, 3, 4]# pop_back is a synonym for pop_rightback = deque.pop_back()print(back) # 4# Raises Exception if deque is emptyempty_deque = Deque()try: empty_deque.pop_right()except Exception as e: print(e) # Empty Deque
Time Complexity: O(1)
View the back element without removing it:
deque = Deque.from_list([10, 20, 30])print(deque.peek_right()) # 30print(len(deque)) # 3 (unchanged)# back is a synonym for peek_rightprint(deque.back()) # 30# Raises Exception if deque is emptyempty_deque = Deque()try: empty_deque.peek_right()except Exception as e: print(e) # Empty deque
DynamicDeque supports all the same methods as Deque:
deque = DynamicDeque()# Append to both ends without capacity concernsfor i in range(50): deque.append_right(i)for i in range(50): deque.append_left(-i)print(len(deque)) # 100# Pop from both endswhile not deque.is_empty(): if len(deque) % 2 == 0: deque.pop_left() else: deque.pop_right()
from dsa.deque import Dequedef is_palindrome(text): """Check if text is a palindrome using deque.""" # Remove spaces and convert to lowercase text = text.replace(" ", "").lower() deque = Deque.from_list(list(text)) while len(deque) > 1: if deque.pop_left() != deque.pop_right(): return False return Trueprint(is_palindrome("racecar")) # Trueprint(is_palindrome("hello")) # Falseprint(is_palindrome("A man a plan a canal Panama")) # True
Find maximum in all sliding windows:
from dsa.deque import DynamicDequedef sliding_window_max(arr, k): """Find maximum in each window of size k.""" deque = DynamicDeque() result = [] for i in range(len(arr)): # Remove elements outside window while not deque.is_empty() and deque.front() <= i - k: deque.pop_left() # Remove smaller elements (they won't be max) while not deque.is_empty() and arr[deque.back()] <= arr[i]: deque.pop_right() deque.append_right(i) # Add to result once we have full window if i >= k - 1: result.append(arr[deque.front()]) return resultarr = [1, 3, -1, -3, 5, 3, 6, 7]print(sliding_window_max(arr, 3)) # [3, 3, 5, 5, 6, 7]
Implement undo/redo with efficient access to both ends:
from dsa.deque import DynamicDequeclass UndoRedoManager: def __init__(self, max_history=50): self.actions = DynamicDeque() self.current_index = -1 self.max_history = max_history def do_action(self, action): # Remove any actions after current position while len(self.actions) > self.current_index + 1: self.actions.pop_right() # Add new action self.actions.append_right(action) self.current_index += 1 # Maintain max history if len(self.actions) > self.max_history: self.actions.pop_left() self.current_index -= 1 def undo(self): if self.current_index >= 0: self.current_index -= 1 return True return False def redo(self): if self.current_index < len(self.actions) - 1: self.current_index += 1 return True return Falsemanager = UndoRedoManager()manager.do_action("Type 'Hello'")manager.do_action("Type ' World'")manager.undo() # Back to 'Hello'manager.redo() # Forward to 'Hello World'
Implement a work-stealing deque for parallel processing:
from dsa.deque import DynamicDequeclass WorkStealingQueue: def __init__(self): self.tasks = DynamicDeque() def add_task(self, task): """Add task to own end (right).""" self.tasks.append_right(task) def get_own_task(self): """Get task from own end (right) - LIFO for cache locality.""" if not self.tasks.is_empty(): return self.tasks.pop_right() return None def steal_task(self): """Steal task from other end (left) - FIFO.""" if not self.tasks.is_empty(): return self.tasks.pop_left() return None def has_tasks(self): return not self.tasks.is_empty()# Worker threadworker = WorkStealingQueue()worker.add_task("Task 1")worker.add_task("Task 2")worker.add_task("Task 3")# Process own tasks (LIFO)print(worker.get_own_task()) # Task 3# Another worker steals (FIFO)print(worker.steal_task()) # Task 1
deque = Deque()# These are equivalentdeque.append_left(1)deque.push_front(1)# These are equivalentdeque.pop_right()deque.pop_back()# These are equivalentdeque.peek_left()deque.front()
When to use Deque: Use when you know the maximum size in advance and want predictable memory usage.When to use DynamicDeque: Use when size varies significantly and you need automatic memory management.