Skip to content
Related Articles

Related Articles

Improve Article

Design Front Middle Back Queue using STL

  • Difficulty Level : Easy
  • Last Updated : 18 Mar, 2021

Design a data structure that supports the following operations in queue efficiently:

  • push__front(x): Insert an element at the front of the queue.
  • push__middle(x): Inserts element at the middle of the queue.
  • push__back(x): Inserts element at the back of the queue.
  • pop__front() Removes the front element of the queue and returns it. If the queue is empty, returns -1.
  • pop__middle(): Removes the middle element of the queue and returns it. If the queue is empty, returns -1.
  • pop__back():Removes the back element of the queue and returns it. If the queue is empty, returns -1.

Examples:

OperationsQueueReturn
push__front(4)4_
push__back(2)4, 2_
push__middle(1)4, 1, 2_
pop_front()1, 24
pop__middle()21
pop__front()_2
pop__front()_-1

Deque-based Approach: The problem can be solved using two deque. The idea is to use two deques. Operation at the back of the queue is to be done at the end of the second deque, and operation at the middle is to be done at the end of the first deque. Follow the steps below to solve the problem:

  • If the size of the first deque is greater than the size of the second deque, then remove the end element of the first deque and add it to the front of the second deque.
  • If the size of the second deque exceeds the size of the first deque by 1, then remove the front element of the second deque and push it at the end of the first deque.
  • If the size of the first deque is greater than the second deque, then remove the back element from the first deque and insert it into the second deque.
  • push__front(x): Insert an element x at the front of the first deque using push_front().
  • push__back(x): Insert an element x at the end of the second deque using push_back()
  • push__middle(x): Insert the element x at the end of the first deque using push_back().
  • pop__front(): Remove the front element of the first deque if the size of deque is greater than 0 using pop_front().
  • pop__back(): Remove the end element of the second deque if the size of deque greater than 0 using pop_back().
  • pop__middle(): Remove the end element of the first deque if the size of deque greater than using pop_back().

Below is the implementation of the above approach:

C++




// C++ pogram to implement
// the above approach
  
#include <bits/stdc++.h>
using namespace std;
  
// Create class Queue.
class Queue {
  
    // Initalize two deques
    deque<int> first, second;
  
    // Function to balance the size of
    // first ans second deque
    void equalizeSizedeque1deque2()
    {
  
        // If size of less than second
        if (first.size() <= second.size())
            return;
  
        // Insert the last element of
        // first deque into second deque
        second.push_front(first.back());
  
        // Pop the front element
        // of the deque
        first.pop_back();
    }
  
    // Function to balance the size of
    // second and first deque
    void equalizeSizedeque2deque1()
    {
  
        // if size of second deque deceed
        // the first deque by 1
        if (second.size() <= first.size() + 1)
            return;
  
        // Insert front element of second
        // deque into the first
        first.push_back(second.front());
  
        // Remove front element of
        // second deque
        second.pop_front();
    }
  
public:
    // Function to insert element
    // at front of queue
    void push__front(int val)
    {
  
        // Insert val into first deque
        first.push_front(val);
  
        // Balancing the size of second
        equalizeSizedeque1deque2();
    }
  
    // Function to insert val
    // into the middle of queue
    void push__middle(int val)
    {
  
        // Insert val into first deque
        first.push_back(val);
  
        // Balancing the size of
        // second deque
        equalizeSizedeque1deque2();
    }
  
    // Function to insert val
    // into back of queue
    void push__back(int val)
    {
  
        // Insert val into second deque
        second.push_back(val);
  
        // Balancing the size of
        // second deque
        equalizeSizedeque2deque1();
    }
  
    // Function to pop front
    // element from queue
    int pop__front()
    {
  
        // If first deque and second
        // deque is empty
        if (first.empty() && second.empty())
            return -1;
  
        int ans;
  
        // If the first deque
        // is empty
        if (first.empty()) {
  
            // Stores front element
            // of second deque
            ans = second.front();
  
            // Pop front element of
            // second deque
            second.pop_front();
        }
        else {
  
            // Stores front element
            // of first deque
            ans = first.front();
  
            // Pop front element of
            // first deque
            first.pop_front();
  
            // Balancing the size of first
            equalizeSizedeque2deque1();
        }
        return ans;
    }
  
    // Function to pop middle
    // element of queue
    int pop__middle()
    {
  
        // If both deques are empty
        if (first.empty() && second.empty())
            return -1;
  
        // Stores mid element
        // of queue
        int ans;
  
        // If size of both deque is equal
        if (first.size() == second.size()) {
  
            // Stores back element
            // of first deque
            ans = first.back();
  
            // Pop back element of
            // first deque
            first.pop_back();
        }
        else {
  
            // Stores front element
            // of second deque
            ans = second.front();
  
            // Pop front element
            // from second deque
            second.pop_front();
        }
        return ans;
    }
  
    // Function to remove mid
    // element from queue
    int pop__back()
    {
  
        // If both the deque are empty
        if (first.empty() && second.empty())
            return -1;
  
        // Stores back element from
        // second deque
        int ans = second.back();
  
        // Pop back element from
        // second deque
        second.pop_back();
  
        // Balancing the size of second
        equalizeSizedeque1deque2();
        return ans;
    }
};
  
// Driver code
int main()
{
    Queue q;
    q.push__front(1);
    q.push__back(2);
    q.push__middle(3);
    cout << q.pop__middle() << " ";
    cout << q.pop__back() << " ";
    cout << q.pop__front() << " ";
    return 0;
}
Output:



3 2 1

Time Complexity Analysis:

push__front(x)pop__front()push__back(x)pop__back()push__middle(x)pop__middle()
O(1)O(1)O(1)O(1)O(1)O(1)

List-based Approach: Follow the steps below to solve the problem:

  • push__front(x): Insert an element x at the front of the list using push_front().
  • push__back(x): Insert an element x at the end of the second list using push_back()
  • push__middle(x): Traverse the list using advance() and then insert the element at mid position of the list using insert()
  • pop__front(): Remove the front element of the list if the size of list greater than 0 using pop_front(), otherwise return -1.
  • pop__back(): Remove the last element of the list if the size of list greater than 0 using pop_back(), otherwise return -1.
  • pop__middle(): If the size of the list greater than 0, then iterate to the middle element of the list using advance() and then erase the element at that position using erase(). Otherwise, return -1.

Below is the implementation of the above approach:

C++




// C++ program to implement
// the above approach
  
#include <bits/stdc++.h>
using namespace std;
  
// Create structure of queue
class Queue {
  
    list<int> l;
  
public:
    // Function to push element
    // at front of the queue
    void push__front(int val)
    {
        l.push_front(val);
    }
  
    // Function to push element
    // at middle of the queue
    void push__middle(int val)
    {
  
        auto itr = l.begin();
  
        // Traverse the list
        advance(itr, l.size() / 2);
  
        // Insert element into
        // middle of the list
        l.insert(itr, val);
    }
  
    // Function to insert element
    // at the back of the queue
    void push__back(int val)
    {
        l.push_back(val);
    }
  
    // Function to pop element from
    // front of the queue
    int pop__front()
    {
  
        // Stores front element
        // of queue
        int val = -1;
        if (l.size()) {
            val = l.front();
            l.pop_front();
        }
        return val;
    }
  
    // Function to pop middle element
    // of the queue
    int pop__middle()
    {
        int val = -1;
        if (l.size()) {
            auto itr = l.begin();
  
            // Traverse the list
            advance(itr, (l.size() - 1) / 2);
            val = *itr;
  
            // Remove mid element
            // from queue
            l.erase(itr);
        }
        return val;
    }
  
    // Function to pop end
    // element of the queue
    int pop__back()
    {
  
        // Stores back element
        // of the queue
        int val = -1;
  
        if (l.size()) {
            val = l.back();
            l.pop_back();
        }
        return val;
    }
};
  
// Drivers code
int main()
{
    Queue q;
    q.push__front(1);
    q.push__back(2);
    q.push__middle(3);
    cout << q.pop__middle() << " ";
    cout << q.pop__back() << " ";
    cout << q.pop__front() << " ";
    return 0;
}
Output:
3 2 1 

Time Complexity Analysis:

push__front(x)pop__front()push__back(x)pop__back()push__middle(x)pop__middle()
O(1)O(1)O(1)O(1)O(N)O(N)

Doubly linked list-based Approach: The problem can also be solved using a doubly-linked list without using STL by storing the address of the head and last node. Follow the steps below to solve the problem:

  • push__front(x):
    • Allocate space for storing the data value x and store the address in the current node pointer
    • Insert the element x by linking the current node between head node and head->next node
    • Increment the capacity by one
  • push__back(x):
    • Allocate space for storing the data value x and store the address in the current node pointer
    • Insert the element x by linking the current node between the last node and last->previous node
    • Increment the capacity by one
  • push__middle(x):
    • Allocate space for storing the data value x and store the address in the current node pointer
    • Initialize a temp pointer of type node
    • Reach the middle element of the doubly linked list by doing temp=temp->next half of current capacity times
    • Now Insert the element x between temp and temp->next by relinking nodes
    • Increment the capacity by one
  • pop__front()
    • If the capacity of size is less than 1 then return -1
    • Otherwise, delete the first node between the head and head->next nodes by relinking nodes
    • Decrement the capacity by one
    • Return value of the deleted element
  • pop__back():
    • If the capacity is less than 1 then return -1
    • Otherwise, delete the end node between the last and last->previous nodes by relinking nodes
    • Decrement the capacity by one
    • Return value of the deleted element
  • pop__middle():
    • Initialize a temp pointer of type node
    • Reach the middle element of the doubly linked list by doing temp=temp->next half of current capacity times
    • Now delete temp node between temp->previous and temp->next nodes by relinking nodes
    • Decrement the capacity by one
    • Return value of the deleted element

    Below is the implementation of the above approach:

    C++




    // C++ program to implement
    // the above approach
      
    #include <iostream>
    using namespace std;
      
    // Structure of Queue class
    class Queue {
    public:
        // Create node structure
        struct node {
      
            // Stores data value
            // of a node
            int value;
      
            // Stores pointer to
            // next node
            node* next;
      
            // Stores pointer to
            // previous node
            node* previous;
      
            // Constructor
            node(int value)
                : value(value), next(NULL), previous(NULL)
            {
            }
        };
      
        typedef struct node node;
      
        // Sizeof the queue
        int capacity = 0;
      
        // Stores address of
        // head node
        node* head;
      
        // Stores address of
        // last node
        node* last;
      
        // Constructor
        Queue()
        {
            head = new node(-1);
            last = new node(-1);
            head->next = last;
            head->previous = NULL;
            last->previous = head;
            last->next = NULL;
        }
      
        // Function to insert element
        // at front of queue
        void push__front(int val)
        {
      
            // Stores address of current node
            node* current = new node(val);
      
            // Stores address of next node
            current->next = head->next;
            head->next->previous = current;
      
            // Update next node of head node
            head->next = current;
      
            // Update previous node
            // of current node
            current->previous = head;
      
            // Update capacity
            capacity++;
        }
      
        // Function to insert element
        // at middle of queue
        void push__middle(int val)
        {
      
            // Stores half of total
            // count of elements
            int middle = capacity / 2;
      
            // Stores head node
            node* temp = head;
      
            // Traverse the list
            for (int i = 0; i < middle; i++) {
      
                // Update temp
                temp = temp->next;
            }
      
            // Initialize a node
            node* current = new node(val);
      
            // Update next node
            // of current node
            current->next = temp->next;
      
            // Update previous node
            // of current node
            current->previous = temp;
            temp->next->previous = current;
            temp->next = current;
      
            // Update capacity
            capacity++;
        }
      
        // Function to insert element at
        // the back of queue
        void push__back(int val)
        {
      
            // Initialize a new node
            node* current = new node(val);
      
            // Update next node of
            // current node
            current->next = last;
      
            // Update previous node of
            // current node
            current->previous = last->previous;
            last->previous->next = current;
            last->previous = current;
      
            // Update capacity
            capacity++;
        }
      
        // Function to pop element
        // from front of queue
        int pop__front()
        {
            if (capacity > 0) {
      
                // Stores data value
                // of next node
                int result
                    = head->next->value;
      
                head->next->next->previous
                    = head;
      
                // Update next node head node
                head->next = head->next->next;
      
                // Update capacity
                capacity--;
                return result;
            }
            return -1;
        }
      
        // Function to pop middle
        // element of queue
        int pop__middle()
        {
      
            // If count of elements
            // greater than 0
            if (capacity > 0) {
      
                // Stores half of total count
                // of elements of queue
                int middle = (capacity + 1) / 2;
      
                // Stores head node
                node* temp = head;
      
                // Traverse the list
                for (int i = 0; i < middle - 1; i++) {
      
                    // Update temp
                    temp = temp->next;
                }
      
                // Stores value of next node
                int result = temp->next->value;
      
                temp->next->next->previous = temp;
                temp->next = temp->next->next;
      
                // Update capacity
                capacity--;
      
                return result;
            }
            return -1;
        }
      
        // Function to remove element
        // from the front of queue
        int pop__back()
        {
      
            // If count of elements in
            // queue greater than 0
            if (capacity > 0) {
      
                // Stores previous data
                // value of last node
                int result = last->previous->value;
                last->previous->previous->next = last;
                last->previous = last->previous->previous;
      
                // Update capacity
                capacity--;
                return result;
            }
            return -1;
        }
    };
      
    // Driver Code
    int main()
    {
        Queue q;
        q.push__front(1);
        q.push__back(2);
        q.push__middle(3);
        cout << q.pop__middle() << " ";
        cout << q.pop__back() << " ";
        cout << q.pop__front() << " ";
        return 0;
    }
    Output:
    3 2 1
    

    Time Complexity Analysis:

    push__front(x)pop__front()push__back(x)pop__back()push__middle(x)pop__middle()
    O(1)O(1)O(1)O(1)O(N)O(N)

    Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.  To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

    In case you wish to attend live classes with experts, please refer DSA Live Classes for Working Professionals and Competitive Programming Live for Students.




    My Personal Notes arrow_drop_up
Recommended Articles
Page :