Skip to content
Related Articles

Related Articles

Improve Article
Save Article
Like Article

Implement Dynamic Multi Stack (K stacks) using only one Data Structure

  • Last Updated : 24 Dec, 2021

In this article, we will see how to create a data structure that can handle multiple stacks with growable size. The data structure needs to handle three operations:

  • push(x, stackNum) = pushes value x to the stack numbered stackNum
  • pop(stackNum) = pop the top element from the stack numbered stackNum
  • top(stackNum) = shows the topmost element of the stack stackNum.

Example:

Suppose the given multi stack is [{1, 2}, {4, 6}, {9, 10}]

Input: push(3, 0), top(0)
push(7, 1), top(1)
pop(2), top(2)

Output: 3, 7, 9

Explanation: When 3 is pushed in stack 0, the stack becomes {1, 2, 3}. So the top element is 3. 
When 7 is pushed in stack 1, the stack becomes {4, 6, 7}. So the top element is 7.
When topmost element is popped from stack 2, the stack becomes {9}. So the topmost element is 9

 

Approach: Follow the approach mentioned below to implement the idea. 

  • Store the size and the top index of every stack in arrays sizes[] and topIndex[].
  • The sizes will be stored in a prefix sum array (using a prefix array sum will help us find the start/size of a stack in O(1) time)
  • If the size of a stack reaches the maximum reserved capacity, expand the reserved size (multiply by 2)
  • If the size of a stack gets down to a quarter of the reserved size shrink the reserved size (divide it by 2)
  • Every time we need to expand/shrink a stack in the data structure, the indexes of other stacks might change so we need to take care of that. That is taken care by incrementing/decrementing value of sizes[] and topIndex[] arrays (we can do that in O(number of stacks) time).

Below is the implementation : 

C++




#include <bits/stdc++.h>
using namespace std;
 
template <typename T>
 
// Class to implement multistack
class MultiStack {
    int numberOfStacks;
    vector<T> values;
    vector<int> sizes, topIndex;
 
public:
    // Constructor to create k stacks
    // (by default 1)
    MultiStack(int k = 1)
        : numberOfStacks(k)
    {
        // reserve 2 elements for each stack first
        values = vector<T>(numberOfStacks << 1);
 
        // For each stack store the index
        // of the element on the top
        // and the size (starting point)
        sizes = vector<int>(numberOfStacks);
        topIndex = vector<int>(numberOfStacks);
 
        // Sizes is a prefix sum vector
        for (int size = 2, i = 0; i < numberOfStacks;
             i++, size += 2)
            sizes[i] = size, topIndex[i] = size - 2;
    }
 
    // Push a value in a stack
    void push(int stackNum, T val)
    {
 
        // Check if the stack is full,
        // if so Expand it
        if (isFull(stackNum))
            Expand(stackNum);
 
        // Add the value to the top of the stack
        // and increment the top index
        values[topIndex[stackNum]++] = val;
    }
 
    // Pop the top value and
    // return it form a stack
    T pop(int stackNum)
    {
 
        // If the stack is empty
        // throw an error
        if (empty(stackNum))
            throw("Empty Stack!");
 
        // Save the top value
        T val = values[topIndex[stackNum] - 1];
 
        // Set top value to default data type
        values[--topIndex[stackNum]] = T();
 
        // Shrink the reserved size if needed
        Shrink(stackNum);
 
        // Return the pop-ed value
        return val;
    }
 
    // Return the top value form a stack
    // Same as pop (but without removal)
    T top(int stackNum)
    {
        if (empty(stackNum))
            throw("Empty Stack!");
        return values[topIndex[stackNum] - 1];
    }
 
    // Return the size of a stack
    // (the number of elements in the stack,
    // not the reserved size)
    int size(int stackNum)
    {
        if (!stackNum)
            return topIndex[0];
        return topIndex[stackNum] - sizes[stackNum - 1];
    }
 
    // Check if a stack is empty or not
    // (has no elements)
    bool empty(int stackNum)
    {
        int offset;
        if (!stackNum)
            offset = 0;
        else
            offset = sizes[stackNum - 1];
        int index = topIndex[stackNum];
        return index == offset;
    }
 
    // Helper function to check
    // if a stack size has reached
    // the reserved size of that stack
    bool isFull(int stackNum)
    {
        int offset = sizes[stackNum];
        int index = topIndex[stackNum];
        return index >= offset;
    }
 
    // Function to expand the reserved size
    // of a stack (multiply by 2)
    void Expand(int stackNum)
    {
 
        // Get the reserved_size of the stack()
        int reserved_size = size(stackNum);
 
        // Update the prefix sums (sizes)
        // and the top index of the next stacks
        for (int i = stackNum + 1; i < numberOfStacks; i++)
            sizes[i] += reserved_size,
                topIndex[i] += reserved_size;
 
        // Update the size of the recent stack
        sizes[stackNum] += reserved_size;
 
        // Double the size of the stack by
        // inserting 'reserved_size' elements
        values.insert(values.begin() + topIndex[stackNum],
                      reserved_size, T());
    }
 
    // Function to shrink the reserved size
    // of a stack (divide by2)
    void Shrink(int stackNum)
    {
 
        // Get the reserved size and the current size
        int reserved_size, current_size;
        if (!stackNum)
            reserved_size = sizes[0],
            current_size = topIndex[0];
        else
            reserved_size
                = sizes[stackNum] - sizes[stackNum - 1],
                current_size
                = topIndex[stackNum] - sizes[stackNum - 1];
 
        // Shrink only if the size is
        // lower than a quarter of the
        // reserved size and avoid shrinking
        // if the reserved size is 2
        if (current_size * 4 > reserved_size
            || reserved_size == 2)
            return;
 
        // Divide the size by 2 and update
        // the prefix sums (sizes) and
        // the top index of the next stacks
        int dif = reserved_size / 2;
        for (int i = stackNum + 1; i < numberOfStacks; i++)
            sizes[i] -= dif, topIndex[i] -= dif;
        sizes[stackNum] -= dif;
 
        // Erase half of the reserved size
        values.erase(values.begin() + topIndex[stackNum],
                     values.begin() + topIndex[stackNum]
                         + dif);
    }
};
 
// Driver code
int main()
{
    // create 3 stacks
    MultiStack<int> MStack(3);
 
    // push elements in stack 0:
    MStack.push(0, 21);
    MStack.push(0, 13);
    MStack.push(0, 14);
 
    // Push one element in stack 1:
    MStack.push(1, 15);
 
    // Push two elements in stack 2:
    MStack.push(2, 1);
    MStack.push(2, 2);
    MStack.push(2, 3);
 
    // Print the top elements of the stacks
    cout << MStack.top(0) << '\n';
    cout << MStack.top(1) << '\n';
    cout << MStack.top(2) << '\n';
 
    return 0;
}
Output
14
15
3

Time complexities:

  • O(1) for top() function.
  • Amortized O(1) for push() and pop() functions.

Auxiliary Space: O(N) where N is the number of stacks


My Personal Notes arrow_drop_up
Recommended Articles
Page :

Start Your Coding Journey Now!