deque doubly Algorithm

The deque doubly algorithm, also known as the double-ended queue algorithm, is a versatile data structure that allows users to efficiently insert or remove elements from both ends of the queue. It is implemented using a doubly-linked list, where each node in the list contains a data element and two pointers, one pointing to the previous node and the other to the next node in the sequence. This structure allows for constant-time access, insertion, and removal of elements at both the front and the rear of the deque, providing a highly flexible and efficient data manipulation mechanism. The deque doubly algorithm is particularly well-suited for applications that require frequent updates at both ends of a sequence or when the data needs to be processed in a last-in-first-out (LIFO) or first-in-first-out (FIFO) manner. In addition to its efficiency and versatility, the deque doubly algorithm also provides several key operations, including push_front, push_back, pop_front, and pop_back. The push_front operation adds an element to the front of the deque, while the push_back operation appends an element to the end of the deque. Conversely, the pop_front operation removes and returns the first element from the deque, and the pop_back operation removes and returns the last element from the deque. These operations can be implemented in constant time, making the deque doubly algorithm an ideal choice for solving complex problems that require dynamic manipulation of data structures. Moreover, the deque doubly algorithm can be easily adapted to support additional functionality, such as searching for specific elements within the deque, iterating through the elements in a specific order, or modifying the deque's size and capacity as needed.
"""
Implementing Deque using DoublyLinkedList ...
Operations:
    1. insertion in the front -> O(1)
    2. insertion in the end -> O(1)
    3. remove from the front -> O(1)
    4. remove from the end -> O(1)
"""


class _DoublyLinkedBase:
    """ A Private class (to be inherited) """

    class _Node:
        __slots__ = "_prev", "_data", "_next"

        def __init__(self, link_p, element, link_n):
            self._prev = link_p
            self._data = element
            self._next = link_n

        def has_next_and_prev(self):
            return " Prev -> {0}, Next -> {1}".format(
                self._prev is not None, self._next is not None
            )

    def __init__(self):
        self._header = self._Node(None, None, None)
        self._trailer = self._Node(None, None, None)
        self._header._next = self._trailer
        self._trailer._prev = self._header
        self._size = 0

    def __len__(self):
        return self._size

    def is_empty(self):
        return self.__len__() == 0

    def _insert(self, predecessor, e, successor):
        # Create new_node by setting it's prev.link -> header
        # setting it's next.link -> trailer
        new_node = self._Node(predecessor, e, successor)
        predecessor._next = new_node
        successor._prev = new_node
        self._size += 1
        return self

    def _delete(self, node):
        predecessor = node._prev
        successor = node._next

        predecessor._next = successor
        successor._prev = predecessor
        self._size -= 1
        temp = node._data
        node._prev = node._next = node._data = None
        del node
        return temp


class LinkedDeque(_DoublyLinkedBase):
    def first(self):
        """ return first element
        >>> d = LinkedDeque()
        >>> d.add_first('A').first()
        'A'
        >>> d.add_first('B').first()
        'B'
        """
        if self.is_empty():
            raise Exception("List is empty")
        return self._header._next._data

    def last(self):
        """ return last element
        >>> d = LinkedDeque()
        >>> d.add_last('A').last()
        'A'
        >>> d.add_last('B').last()
        'B'
        """
        if self.is_empty():
            raise Exception("List is empty")
        return self._trailer._prev._data

    # DEque Insert Operations (At the front, At the end)

    def add_first(self, element):
        """ insertion in the front
        >>> LinkedDeque().add_first('AV').first()
        'AV'
        """
        return self._insert(self._header, element, self._header._next)

    def add_last(self, element):
        """ insertion in the end
        >>> LinkedDeque().add_last('B').last()
        'B'
        """
        return self._insert(self._trailer._prev, element, self._trailer)

    # DEqueu Remove Operations (At the front, At the end)

    def remove_first(self):
        """ removal from the front
        >>> d = LinkedDeque()
        >>> d.is_empty()
        True
        >>> d.remove_first()
        Traceback (most recent call last):
           ...
        IndexError: remove_first from empty list
        >>> d.add_first('A') # doctest: +ELLIPSIS
        <linked_list.deque_doubly.LinkedDeque object at ...
        >>> d.remove_first()
        'A'
        >>> d.is_empty()
        True
        """
        if self.is_empty():
            raise IndexError("remove_first from empty list")
        return self._delete(self._header._next)

    def remove_last(self):
        """ removal in the end
        >>> d = LinkedDeque()
        >>> d.is_empty()
        True
        >>> d.remove_last()
        Traceback (most recent call last):
           ...
        IndexError: remove_first from empty list
        >>> d.add_first('A') # doctest: +ELLIPSIS
        <linked_list.deque_doubly.LinkedDeque object at ...
        >>> d.remove_last()
        'A'
        >>> d.is_empty()
        True
        """
        if self.is_empty():
            raise IndexError("remove_first from empty list")
        return self._delete(self._trailer._prev)

LANGUAGE:

DARK MODE: