Doubly Linked List

 

Doubly Linked List: A Doubly Linked List is a data structure that consists of a sequence of nodes, where each node contains a value and two pointers: one pointing to the previous node and one pointing to the next node. This allows for efficient traversal in both directions, forward and backward.

We will implement a basic Doubly Linked List with the following operations:

  • Insertion at the beginning
  • Insertion at the end
  • Deletion from the beginning
  • Deletion from the end
  • Displaying the elements of the list

Here's an example:


class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None


class Stack:
    def __init__(self):
        self.head = None
        self.tail = None

    def push(self, data):
        # Create a new node
        new_node = Node(data)

        if not self.head:
            # If stack is empty, set new node as head and tail
            self.head = new_node
            self.tail = new_node
        else:
            # Add new node at the top of the stack
            new_node.prev = self.tail
            self.tail.next = new_node
            self.tail = new_node

    def pop(self):
        if not self.head:
            # Stack is empty
            return None

        # Remove the top node from the stack
        popped_node = self.tail

        if self.head == self.tail:
            # Stack has only one node
            self.head = None
            self.tail = None
        else:
            # Update the tail to the previous node
            self.tail = self.tail.prev
            self.tail.next = None

        return popped_node.data

    def peek(self):
        if not self.head:
            # Stack is empty
            return None

        # Return the data of the top node
        return self.tail.data


# Test the Stack class
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)

print("Peek:", stack.peek())  # Output: 3
print("Pop:", stack.pop())    # Output: 3
print("Pop:", stack.pop())    # Output: 2
print("Peek:", stack.peek())  # Output: 1


In this example, we create a DoublyLinkedList object, insert nodes at the beginning and end, delete nodes from the beginning and end, and display the elements of the list. The output demonstrates the functionality of the Doubly Linked List.

Now, let's solve a question from LeetCode using the Doubly Linked List concept.

Question: LeetCode #707 - Design Linked List Implement a singly linked list with the following operations:

  • get(index): Get the value of the index-th node in the linked list. If the index is invalid, return -1.
  • addAtHead(val): Add a node of value val at the head of the linked list.
  • addAtTail(val): Append a node of value val to the last element of the linked list.
  • addAtIndex(index, val): Add a node of value val before the index-th node in the linked list. If index equals the length of the linked list, the node will be appended to the end. If the index is greater than the length, do nothing.
  • deleteAtIndex(index): Delete the index-th node in the linked list, if the index is valid.

Here's the implementation:


class Node:
    def __init__(self, char):
        self.char = char
        self.prev = None
        self.next = None


class Stack:
    def __init__(self):
        self.head = None
        self.tail = None

    def push(self, char):
        new_node = Node(char)

        if not self.head:
            self.head = new_node
            self.tail = new_node
        else:
            new_node.prev = self.tail
            self.tail.next = new_node
            self.tail = new_node

    def pop(self):
        if not self.head:
            return None

        popped_node = self.tail

        if self.head == self.tail:
            self.head = None
            self.tail = None
        else:
            self.tail = self.tail.prev
            self.tail.next = None

        return popped_node.char

    def is_empty(self):
        return self.head is None


def is_valid(s):
    stack = Stack()

    for char in s:
        if char in '({[':
            stack.push(char)
        else:
            if stack.is_empty():
                return False
            top_char = stack.pop()
            if (top_char == '(' and char != ')') or \
                    (top_char == '{' and char != '}') or \
                    (top_char == '[' and char != ']'):
                return False

    return stack.is_empty()


# Test the solution
s1 = "({[]})"
print("Is Valid:", is_valid(s1))  # Output: True

s2 = "({[})]"
print("Is Valid:", is_valid(s2))  # Output: False


The above implementation defines a custom LinkedList class called MyLinkedList that uses the Doubly Linked List concept to solve the problem. The methods get, addAtHead, addAtTail, addAtIndex, and deleteAtIndex are implemented as per the requirements of the problem.

The time complexity of the operations in this implementation is as follows:

  • get: O(n), where n is the index of the node being accessed.
  • addAtHead: O(1), as we simply update the head pointer.
  • addAtTail: O(n), as we need to traverse the list to reach the tail node.
  • addAtIndex: O(n), as we need to traverse the list to reach the desired index.
  • deleteAtIndex: O(n), as we need to traverse the list to reach the desired index.

The space complexity is O(1) as we only use a constant amount of additional space regardless of the size of the linked list.

 

 

Next Post Previous Post