How to efficiently implement k stacks in a single array?
Last Updated :
24 May, 2023
We have discussed space-efficient implementation of 2 stacks in a single array. In this post, a general solution for k stacks is discussed. Following is the detailed problem statement. Create a data structure kStacks that represents k stacks. Implementation of kStacks should use only one array, i.e., k stacks should use the same array for storing elements.
The following functions must be supported by k Stacks. push(int x, int sn) –> pushes x to stack number ‘sn’ where sn is from 0 to k-1 pop(int sn) –> pops an element from stack number ‘sn’ where sn is from 0 to k-1
Method 1 (Divide the array in slots of size n/k) A simple way to implement k stacks is to divide the array in k slots of size n/k each, and fix the slots for different stacks, i.e., use arr[0] to arr[n/k-1] for first stack, and arr[n/k] to arr[2n/k-1] for stack2 where arr[] is the array to be used to implement two stacks and size of array be n. The problem with this method is inefficient use of array space. A stack push operation may result in stack overflow even if there is space available in arr[]. For example, say the k is 2 and array size (n) is 6 and we push 3 elements to first and do not push anything to second stack. When we push 4th element to first, there will be overflow even if we have space for 3 more elements in array.
Method 2 (A space-efficient implementation) The idea is to use two extra arrays for efficient implementation of k stacks in an array. This may not make much sense for integer stacks, but stack items can be large for example stacks of employees, students, etc where every item is of hundreds of bytes. For such large stacks, the extra space used is comparatively very less as we use two integer arrays as extra space.
Following are the two extra arrays are used:
1) top[]: This is of size k and stores indexes of top elements in all stacks.
2) next[]: This is of size n and stores indexes of next item for the items in array arr[].
Here arr[] is actual array that stores k stacks. Together with k stacks, a stack of free slots in arr[] is also maintained. The top of this stack is stored in a variable ‘free’. All entries in top[] are initialized as -1 to indicate that all stacks are empty. All entries next[i] are initialized as i+1 because all slots are free initially and pointing to next slot. Top of free stack, ‘free’ is initialized as 0.
Algorithm:
- Initialize an array of size k to keep track of the top element of each stack.
- Initialize an array next of size n, where n is the total size of the array that will hold the k stacks. Set the value of next[i] to i+1 for all 0 ? i < n-1, and next[n-1] to -1. This array will be used to keep track of the next element in the stack.
- Initialize an array top of size k to store the index of the top element of each stack. Set the value of top[i] to -1 for all 0 ? i < k.
- To push an element onto the i-th stack, do the following:
- Check if the array is full by checking if next[0] is -1. If it is, return an error message indicating that the stack is full.
- Set the value of next[0] to top[i].
- Set the value of top[i] to 0.
- Set the value of next[top[i]] to the new element’s index.
- Increment the value of top[i] by the block size.
- To pop an element from the i-th stack, do the following:
- Check if the stack is empty by checking if top[i] is -1. If it is, return an error message indicating that the stack is empty.
- Decrement the value of top[i] by the block size.
- Set the value of next[top[i]] to -1.
- Return the element at the index top[i] + block size – 1.
Following is the implementation of the above idea.
C++
#include<bits/stdc++.h>
using namespace std;
class kStacks
{
int *arr;
int *top;
int *next;
int n, k;
int free ;
public :
kStacks( int k, int n);
bool isFull() { return ( free == -1); }
void push( int item, int sn);
int pop( int sn);
bool isEmpty( int sn) { return (top[sn] == -1); }
};
kStacks::kStacks( int k1, int n1)
{
k = k1, n = n1;
arr = new int [n];
top = new int [k];
next = new int [n];
for ( int i = 0; i < k; i++)
top[i] = -1;
free = 0;
for ( int i=0; i<n-1; i++)
next[i] = i+1;
next[n-1] = -1;
}
void kStacks::push( int item, int sn)
{
if (isFull())
{
cout << "\nStack Overflow\n" ;
return ;
}
int i = free ;
free = next[i];
next[i] = top[sn];
top[sn] = i;
arr[i] = item;
}
int kStacks::pop( int sn)
{
if (isEmpty(sn))
{
cout << "\nStack Underflow\n" ;
return INT_MAX;
}
int i = top[sn];
top[sn] = next[i];
next[i] = free ;
free = i;
return arr[i];
}
int main()
{
int k = 3, n = 10;
kStacks ks(k, n);
ks.push(15, 2);
ks.push(45, 2);
ks.push(17, 1);
ks.push(49, 1);
ks.push(39, 1);
ks.push(11, 0);
ks.push(9, 0);
ks.push(7, 0);
cout << "Popped element from stack 2 is " << ks.pop(2) << endl;
cout << "Popped element from stack 1 is " << ks.pop(1) << endl;
cout << "Popped element from stack 0 is " << ks.pop(0) << endl;
return 0;
}
|
Java
public class GFG
{
static class KStack
{
int arr[];
int top[];
int next[];
int n, k;
int free;
KStack( int k1, int n1)
{
k = k1;
n = n1;
arr = new int [n];
top = new int [k];
next = new int [n];
for ( int i = 0 ; i < k; i++)
top[i] = - 1 ;
free = 0 ;
for ( int i = 0 ; i < n - 1 ; i++)
next[i] = i + 1 ;
next[n - 1 ] = - 1 ;
}
boolean isFull()
{
return (free == - 1 );
}
void push( int item, int sn)
{
if (isFull())
{
System.out.println( "Stack Overflow" );
return ;
}
int i = free;
free = next[i];
next[i] = top[sn];
top[sn] = i;
arr[i] = item;
}
int pop( int sn)
{
if (isEmpty(sn))
{
System.out.println( "Stack Underflow" );
return Integer.MAX_VALUE;
}
int i = top[sn];
top[sn] = next[i];
next[i] = free;
free = i;
return arr[i];
}
boolean isEmpty( int sn)
{
return (top[sn] == - 1 );
}
}
public static void main(String[] args)
{
int k = 3 , n = 10 ;
KStack ks = new KStack(k, n);
ks.push( 15 , 2 );
ks.push( 45 , 2 );
ks.push( 17 , 1 );
ks.push( 49 , 1 );
ks.push( 39 , 1 );
ks.push( 11 , 0 );
ks.push( 9 , 0 );
ks.push( 7 , 0 );
System.out.println( "Popped element from stack 2 is " + ks.pop( 2 ));
System.out.println( "Popped element from stack 1 is " + ks.pop( 1 ));
System.out.println( "Popped element from stack 0 is " + ks.pop( 0 ));
}
}
|
Python3
class KStacks:
def __init__( self , k, n):
self .k = k
self .n = n
self .arr = [ 0 ] * self .n
self .top = [ - 1 ] * self .k
self .free = 0
self . next = [i + 1 for i in range ( self .n)]
self . next [ self .n - 1 ] = - 1
def isEmpty( self , sn):
return self .top[sn] = = - 1
def isFull( self ):
return self .free = = - 1
def push( self , item, sn):
if self .isFull():
print ( "Stack Overflow" )
return
insert_at = self .free
self .free = self . next [ self .free]
self .arr[insert_at] = item
self . next [insert_at] = self .top[sn]
self .top[sn] = insert_at
def pop( self , sn):
if self .isEmpty(sn):
return None
top_of_stack = self .top[sn]
self .top[sn] = self . next [ self .top[sn]]
self . next [top_of_stack] = self .free
self .free = top_of_stack
return self .arr[top_of_stack]
def printstack( self , sn):
top_index = self .top[sn]
while (top_index ! = - 1 ):
print ( self .arr[top_index])
top_index = self . next [top_index]
if __name__ = = "__main__" :
kstacks = KStacks( 3 , 10 )
kstacks.push( 15 , 2 )
kstacks.push( 45 , 2 )
kstacks.push( 17 , 1 )
kstacks.push( 49 , 1 )
kstacks.push( 39 , 1 )
kstacks.push( 11 , 0 )
kstacks.push( 9 , 0 )
kstacks.push( 7 , 0 )
print ( "Popped element from stack 2 is " +
str (kstacks.pop( 2 )))
print ( "Popped element from stack 1 is " +
str (kstacks.pop( 1 )))
print ( "Popped element from stack 0 is " +
str (kstacks.pop( 0 )))
kstacks.printstack( 0 )
|
C#
using System;
public class GFG
{
public class KStack
{
public int [] arr;
public int [] top;
public int [] next;
public int n, k;
public int free;
public KStack( int k1, int n1)
{
k = k1;
n = n1;
arr = new int [n];
top = new int [k];
next = new int [n];
for ( int i = 0; i < k; i++)
{
top[i] = -1;
}
free = 0;
for ( int i = 0; i < n - 1; i++)
{
next[i] = i + 1;
}
next[n - 1] = -1;
}
public virtual bool Full
{
get
{
return (free == -1);
}
}
public virtual void push( int item, int sn)
{
if (Full)
{
Console.WriteLine( "Stack Overflow" );
return ;
}
int i = free;
free = next[i];
next[i] = top[sn];
top[sn] = i;
arr[i] = item;
}
public virtual int pop( int sn)
{
if (isEmpty(sn))
{
Console.WriteLine( "Stack Underflow" );
return int .MaxValue;
}
int i = top[sn];
top[sn] = next[i];
next[i] = free;
free = i;
return arr[i];
}
public virtual bool isEmpty( int sn)
{
return (top[sn] == -1);
}
}
public static void Main( string [] args)
{
int k = 3, n = 10;
KStack ks = new KStack(k, n);
ks.push(15, 2);
ks.push(45, 2);
ks.push(17, 1);
ks.push(49, 1);
ks.push(39, 1);
ks.push(11, 0);
ks.push(9, 0);
ks.push(7, 0);
Console.WriteLine( "Popped element from stack 2 is " + ks.pop(2));
Console.WriteLine( "Popped element from stack 1 is " + ks.pop(1));
Console.WriteLine( "Popped element from stack 0 is " + ks.pop(0));
}
}
|
Javascript
<script>
class KStack {
constructor(k1 , n1)
{
this .k = k1;
this .n = n1;
this .arr = Array(n).fill(0);
this .top = Array(k).fill(-1);
this .next = Array(n).fill(0);
this .free = 0;
for ( var i = 0; i < n - 1; i++)
this .next[i] = i + 1;
this .next[n - 1] = -1;
}
isFull() {
return ( this .free == -1);
}
push(item , sn)
{
if ( this .isFull()) {
document.write( "Stack Overflow" );
return ;
}
var i = this .free;
this .free = this .next[i];
this .next[i] = this .top[sn];
this .top[sn] = i;
this .arr[i] = item;
}
pop(sn)
{
if ( this .isEmpty(sn)) {
document.write( "Stack Underflow" );
return Number.MAX_VALUE;
}
var i = this .top[sn];
this .top[sn] = this .next[i];
this .next[i] = this .free;
this .free = i;
return this .arr[i];
}
isEmpty(sn) {
return ( this .top[sn] == -1);
}
}
var k = 3;
n = 10;
var ks = new KStack(k, n);
ks.push(15, 2);
ks.push(45, 2);
ks.push(17, 1);
ks.push(49, 1);
ks.push(39, 1);
ks.push(11, 0);
ks.push(9, 0);
ks.push(7, 0);
document.write( "Popped element from stack 2 is " + ks.pop(2));
document.write( "<br/>Popped element from stack 1 is " + ks.pop(1));
document.write( "<br/>Popped element from stack 0 is " + ks.pop(0));
</script>
|
Output
Popped element from stack 2 is 45
Popped element from stack 1 is 39
Popped element from stack 0 is 7
Time complexities of operations push() and pop() is O(1). The best part of above implementation is, if there is a slot available in stack, then an item can be pushed in any of the stacks, i.e., no wastage of space.
Time Complexity of top() operation is also O(1)
Time Complexity: O(N), as we are using a loop to traverse N times.
Auxiliary Space: O(N), as we are using extra space for the stack.
Please Login to comment...