How to implement 3 stacks with one array?

AlgorithmData StructuresStack

Algorithm Problem Overview


Sometimes, I come across the following interview question: How to implement 3 stacks with one array ? Of course, any static allocation is not a solution.

Algorithm Solutions


Solution 1 - Algorithm

Space (not time) efficient. You could:

  1. Define two stacks beginning at the array endpoints and growing in opposite directions.

  2. Define the third stack as starting in the middle and growing in any direction you want.

  3. Redefine the Push op, so that when the operation is going to overwrite other stack, you shift the whole middle stack in the opposite direction before Pushing.

You need to store the stack top for the first two stacks, and the beginning and end of the third stack in some structure.

Edit

alt text

Above you may see an example. The shifting is done with an equal space partitioning policy, although other strategies could be chosen depending upon your problem heuristics.

Edit

Following @ruslik's suggestion, the middle stack could be implemented using an alternating sequence for subsequent pushes. The resulting stack structure will be something like:

>
> | Elem 6 | Elem 4 | Elem 2 | Elem 0 | Elem 1 | Elem 3 | Elem 5 |
>

In this case, you'll need to store the number n of elements on the middle stack and use the function:

f[n_] := 1/4 ( (-1)^n (-1 + 2 n) + 1) + BS3  

to know the next array element to use for this stack.

Although probably this will lead to less shifting, the implementation is not homogeneous for the three stacks, and inhomogeneity (you know) leads to special cases, more bugs and difficulties to maintain code.

Solution 2 - Algorithm

As long as you try to arrange all items from one stack together at one "end" of the array, you're lacking space for the third stack.

However, you could "intersperse" the stack elements. Elements of the first stack are at indices i * 3, elements of the second stack are at indices i * 3 + 1, elements of the third stack are at indices i * 3 + 2 (where i is an integer).

+----+----+----+----+----+----+----+----+----+----+----+----+----+..
| A1 : B1 : C1 | A2 : B2 : C2 |    : B3 | C3 |    : B4 :    |    :  
+----+----+----+----+----+----+----+----+----+----+----+----+----+..
                  ^                        ^         ^
                  A´s top                  C´s top   B´s top

Of course, this scheme is going to waste space, especially when the stacks have unequal sizes. You could create arbitrarily complex schemes similar to the one described above, but without knowing any more constraints for the posed question, I'll stop here.

Update:

Due to the comments below, which do have a very good point, it should be added that interspersing is not necessary, and may even degrade performance when compared to a much simpler memory layout such as the following:

+----+----+----+----+----+----+----+----+----+----+----+----+----+..
| A1 : A2 :    :    :    | B1 : B2 : B3 : B4 :    | C1 : C2 : C3 :  
+----+----+----+----+----+----+----+----+----+----+----+----+----+..
       ^                                  ^                    ^
       A´s top                            B´s top              C´s top

i.e. giving each stack it's own contiguous block of memory. If the real question is indeed to how to make the best possible use of a fixed amount of memory, in order to not limit each stack more than necessary, then my answer isn't going to be very helpful.

In that case, I'd go with @belisarius' answer: One stack goes to the "bottom" end of the memory area, growing "upwards"; another stack goes to the "top" end of the memory area, growing "downwards", and one stack is in the middle that grows in any direction but is able to move when it gets too close to one of the other stacks.

Solution 3 - Algorithm

Maintain a single arena for all three stacks. Each element pushed onto the stack has a backwards pointer to its previous element. The bottom of each stack has a pointer to NULL/None.

The arena maintains a pointer to the next item in the free space. A push adds this element to the respective stack and marks it as no longer in the free space. A pop removes the element from the respective stack and adds it to the free list.

From this sketch, elements in stacks need a reverse pointer and space for the data. Elements in the free space need two pointers, so the free space is implemented as a doubly linked list.

The object containing the three stacks needs a pointer to the top of each stack plus a pointer to the head of the free list.

This data structure uses all the space and pushes and pops in constant time. There is overhead of a single pointer for all data elements in a stack and the free list elements use the maximum of (two pointers, one pointer + one element).


Later: python code goes something like this. Note use of integer indexes as pointers.

class StackContainer(object):
    def __init__(self, stack_count=3, size=256):
        self.stack_count = stack_count
        self.stack_top = [None] * stack_count
        self.size = size
        # Create arena of doubly linked list
        self.arena = [{'prev': x-1, 'next': x+1} for x in range(self.size)]
        self.arena[0]['prev'] = None
        self.arena[self.size-1]['next'] = None
        self.arena_head = 0
    
    def _allocate(self):
        new_pos = self.arena_head
        free = self.arena[new_pos]
        next = free['next']
        if next:
            self.arena[next]['prev'] = None
            self.arena_head = next
        else:
            self.arena_head = None
        return new_pos
    
    def _dump(self, stack_num):
        assert 0 <= stack_num < self.stack_count
        curr = self.stack_top[stack_num]
        while curr is not None:
            d = self.arena[curr]
            print '\t', curr, d
            curr = d['prev']
    
    def _dump_all(self):
        print '-' * 30
        for i in range(self.stack_count):
            print "Stack %d" % i
            self._dump(i)
    
    def _dump_arena(self):
        print "Dump arena"
        curr = self.arena_head
        while curr is not None:
            d = self.arena[curr]
            print '\t', d
            curr = d['next']
    
    def push(self, stack_num, value):
        assert 0 <= stack_num < self.stack_count
        # Find space in arena for new value, update pointers
        new_pos = self._allocate()
        # Put value-to-push into a stack element
        d = {'value': value, 'prev': self.stack_top[stack_num], 'pos': new_pos}
        self.arena[new_pos] = d
        self.stack_top[stack_num] = new_pos
    
    def pop(self, stack_num):
        assert 0 <= stack_num < self.stack_count
        top = self.stack_top[stack_num]
        d = self.arena[top]
        assert d['pos'] == top
        self.stack_top[stack_num] = d['prev']
        arena_elem = {'prev': None, 'next': self.arena_head}
        # Link the current head to the new head
        head = self.arena[self.arena_head]
        head['prev'] = top
        # Set the curr_pos to be the new head
        self.arena[top] = arena_elem
        self.arena_head = top
        return d['value']

if __name__ == '__main__':
    sc = StackContainer(3, 10)
    sc._dump_arena()
    sc.push(0, 'First')
    sc._dump_all()
    sc.push(0, 'Second')
    sc.push(0, 'Third')
    sc._dump_all()
    sc.push(1, 'Fourth')
    sc._dump_all()
    print sc.pop(0)
    sc._dump_all()
    print sc.pop(1)
    sc._dump_all()

Solution 4 - Algorithm

I have a solution for this question. The following program makes the best use of the array (in my case, an array of StackNode Objects). Let me know if you guys have any questions about this. [It's pretty late out here, so i didn't bother to document the code - I know, I should :) ]

public class StackNode {
	int value;
	int prev;

	StackNode(int value, int prev) {
		this.value = value;
		this.prev = prev;
	}
}


public class StackMFromArray {
	private StackNode[] stackNodes = null;
	private static int CAPACITY = 10;
	private int freeListTop = 0;
	private int size = 0;
	private int[] stackPointers = { -1, -1, -1 };

	StackMFromArray() {
		stackNodes = new StackNode[CAPACITY];
		initFreeList();
	}

	private void initFreeList() {
		for (int i = 0; i < CAPACITY; i++) {
			stackNodes[i] = new StackNode(0, i + 1);
		}
	}

	public void push(int stackNum, int value) throws Exception {
		int freeIndex;
		int currentStackTop = stackPointers[stackNum - 1];
		freeIndex = getFreeNodeIndex();
		StackNode n = stackNodes[freeIndex];
		n.prev = currentStackTop;
		n.value = value;
		stackPointers[stackNum - 1] = freeIndex;
	}

	public StackNode pop(int stackNum) throws Exception {
		int currentStackTop = stackPointers[stackNum - 1];
		if (currentStackTop == -1) {
			throw new Exception("UNDERFLOW");
		}

		StackNode temp = stackNodes[currentStackTop];
		stackPointers[stackNum - 1] = temp.prev;
		freeStackNode(currentStackTop);
		return temp;
	}

	private int getFreeNodeIndex() throws Exception {
		int temp = freeListTop;

		if (size >= CAPACITY)
			throw new Exception("OVERFLOW");

		freeListTop = stackNodes[temp].prev;
		size++;
		return temp;
	}

	private void freeStackNode(int index) {
		stackNodes[index].prev = freeListTop;
		freeListTop = index;
		size--;
	}

	public static void main(String args[]) {
                    // Test Driver
		StackMFromArray mulStack = new StackMFromArray();
		try {
			mulStack.push(1, 11);
			mulStack.push(1, 12);
			mulStack.push(2, 21);
			mulStack.push(3, 31);
			mulStack.push(3, 32);
			mulStack.push(2, 22);
			mulStack.push(1, 13);
			StackNode node = mulStack.pop(1);
			node = mulStack.pop(1);
			System.out.println(node.value);
			mulStack.push(1, 13);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Solution 5 - Algorithm

For simplicity if not very efficient memory usage, you could[*] divide the array up into list nodes, add them all to a list of free nodes, and then implement your stacks as linked lists, taking nodes from the free list as required. There's nothing special about the number 3 in this approach, though.

[*] in a low-level language where memory can be used to store pointers, or if the stack elements are of a type such as int that can represent an index into the array.

Solution 6 - Algorithm

A variant on an earlier answer: stack #1 grows from the left, and stack #2 grows from the right.

Stack #3 is in the center, but the elements grow in alternate order to the left and right. If N is the center index, the stack grows as: N, N-1, N+1, N-2, N+2, etc. A simple function converts the stack index to an array index.

Solution 7 - Algorithm

There are many solutions to this problem already stated on this page. The fundamental questions, IMHO are:

  • How long does each push/pop operation take?

  • How much space is used? Specifically, what is the smallest number of elements that can be pushed to the three stacks to cause the data structure to run out of space?

As far as I can tell, each solution already posted on this page either can take up to linear time for a push/pop or can run out of space with a linear number of spaces still empty.

In this post, I will reference solutions that perform much better, and I will present the simplest one.


In order to describe the solution space more carefully, I will refer to two functions of a data structure in the following way:

A structure that takes O(f(n)) amortized time to perform a push/pop and does not run out of space unless the three stacks hold at least n - O(g(n)) items will be referred to as an (f,g) structure. Smaller f and g are better. Every structure already posted on this page has n for either the time or the space. I will demonstrate a (1,√n) structure.

This is all based on:

  • Michael L. Fredman and Deborah L. Goldsmith, "Three Stacks", in Journal of Algorithms, Volume 17, Issue 1, July 1994, Pages 45-70

  • An earlier version appeared in the 29th Annual Symposium on Foundations of Computer Science (FOCS) in 1988

  • Deborah Louise Goldsmith's PhD thesis from University of California, San Diego, Department of Electrical Engineering/Computer Science in 1987, "Efficient memory management for >= 3 stacks"

They show, though I will not present, a (log n/log S,S) structure for any S. This is equivalent to a (t, n1/t) structure for any t. I will show a simplified version that is a (1,√n) structure.


Divide the array up into blocks of size Θ(√n). The blocks are numbered from 1 to Θ(√n), and the number of a block is called its "address". An address can be stored in an array slot instead of a real item. An item within a given block can be referred to with a number less than O(√n), and such a number is called an index. An index will also fit in an array slot.

The first block will be set aside for storing addresses and indexes, and no other block will store any addresses or indexes. The first block is called the directory. Every non-directory block will either be empty or hold elements from just one of the three stacks; that is, no block will have two elements from different stacks. Additionally, every stack will have at most one block that is partially filled -- all other blocks associated with a stack will be completely full or completely empty.

As long as there is an empty block, a push operation will be permitted to any stack. Pop operations are always permitted. When a push operation fails, the data structure is full. At that point, the number of slots not containing elements from one of the stacks is at most O(√n): two partially-filled blocks from the stacks not being pushed to, and one directory block.

Every block is ordered so that the elements closer to the front of the block (lower indexes) are closer to the bottom of the stack.

The directory holds:

  • Three addresses for the blocks at the top of the three stacks, or 0 if there are no blocks in a particular stack yet

  • Three indexes for the element at the top of the three stacks, or 0 if there are no items in a particular stack yet.

  • For each full or partially full block, the address of the block lower than it in the same stack, or 0 if it is the lowest block in the stack.

  • The address of a free block, called the leader block, or 0 if there are no free blocks

  • For each free block, the address of another free block, or 0 if there are no more free blocks

These last two constitute a stack, stored as a singly-linked list, of free blocks. That is, following the addresses of free blocks starting with the leader block will give a path through all the free blocks, ending in a 0.

To push an item onto a stack, find its top block and top element within that block using the directory. If there is room in that block, put the item there and return.

Otherwise, pop the stack of free blocks by changing the address of the leader block to the address of the next free block in the free block stack. Change the address and index for the stack to be the address of the just-popped free block and 1, respectively. Add the item to the just-popped block at index 1, and return.

All operations take O(1) time. Pop is symmetric.

Solution 8 - Algorithm

I think you should divide array in 3 pieces, making head of first stack at 0, head of second stack at n/3, head of 3rd stack at n-1.

so implement push operation on :

  1. first & second stack make i++ and for 3rd stack make i--;
  2. If you encounter that first stack have no space to push, shift 2nd stack k/3 positions forward. Where k is the number of positions left to be filled in array.
  3. If you encounter that second stack have no space to push, shift 2nd stack 2*k/3 positions backward. Where k is the number of positions left to be filled in array.
  4. If you encounter that third stack have no space to push, shift 2nd stack 2*k/3 positions backward. Where k is the number of positions left to be filled in array.

We are shifting k/3 and 2*k/3 when no space is left so that after shifting of middle stack, each stack have equal space available for use.

Solution 9 - Algorithm

Store the stack in the area in such way when first stack goes into index 0, then 0+3=3, then 3+3=6...; the second one goes into indexes 1, 1+3=4, 4+3=7...; the the third one goes into indexes 2, 2+3=5, 5+3=8 So if we mark the first stack elements with a, as one with b and there with c we get: a1 b1 c1 a2 b2 c2 a3 b3 c3...

There could be gaps but we always know the top indexes which are stored in 3-element topIndex array.

Solution 10 - Algorithm

Partitioning the array into 3 parts in not a good idea as it will give overflow if there are many elements in stack1 and very few elements in the other two.

My idea:

Keep three pointers ptr1, ptr2, ptr3 to point the top element of respective stacks.

Initially ptr1 = ptr2 = ptr3 = -1;

In the array, the even indexed element will store the value and odd indexed element will store the index of previous element of that stack.

For example,

s1.push(1);
s2.push(4);
s3.push(3);
s1.push(2);
s3.push(7);
s1.push(10);
s1.push(5);

then our array looks like:

1, -1, 4, -1, 3, -1, 2, 0, 7, 4, 10, 6, 5, 10

and the values of pointers are:

ptr1 = 12, ptr2 = 2 , ptr3 = 8

Solution 11 - Algorithm

Solution: Implementing two stacks is easy. First stack grows from start to end while second one grows from end to start. Overflow for any of them will not happen unless there really is no space left on the array.

For three stacks, following is required: An auxiliary array to maintain the parent for each node. Variables to store the current top of each stack. With these two in place, data from all the stacks can be interspersed in the original array and one can still do push/pop/size operations for all the stacks.

enter image description here

When inserting any element, insert it at the end of all the elements in the normal array. Store current-top of that stack as parent for the new element (in the parents' array) and update current-top to the new position.

When deleting, insert NULL in the stacks array for the deleted element and reset stack-top for that stack to the parent.

When the array is full, it will have some holes corresponding to deleted elements. At this point, either the array can be compacted to bring all free space together or a linear search can be done for free space when inserting new elements.

for further details refer this link:- https://coderworld109.blogspot.in/2017/12/how-to-implement-3-stacks-with-one-array.html

Solution 12 - Algorithm

Here is my solution of N stacks in a single array.

Some constraints will be here. that size of the array will not be less than of the number of stacks.

I have used to customize exception class StackException in my solution. You can change the exception class for running the programme.

For multiple stacks in an array, I managed pointers to another array.

package com.practice.ds.stack;

import java.util.Scanner;
import java.util.logging.Logger;

/** Multiple stacks in a single array */
public class MultipleStack {

	private static Logger logger = Logger.getLogger("MultipleStack");

	private int[] array;
	private int size = 10;
	private int stackN = 1;
	private int[] pointer;

	public MultipleStack() {
		this.array = new int[size];
		this.pointer = new int[1];
	}

	public MultipleStack(int size, int stackN) throws StackException {
		if (stackN > size)
			throw new StackException("Input mismatch ! no of stacks can't be larger than size ");
		this.size = size;
		this.stackN = stackN;
		init();
	}

	private void init() {
		if (size <= 0) {
			logger.info("Initialize size is " + size + " so assiginig defalt size ");
			this.size = 10;
		}
		if (stackN < 1) {
			logger.info("Initialize no of Stack is " + size + " so assiginig defalt");
			this.stackN = 1;
		}
		this.array = new int[size];
		this.pointer = new int[stackN];
		initializePointer();
	}

	private void initializePointer() {
		for (int i = 0; i < stackN; i++)
			pointer[i] = (int)(i * Math.ceil(size / stackN) - 1);
	}

	public void push(int item, int sn) throws StackException {
		if (full(sn))
			throw new StackException(sn + " is overflowed !");
		int stkPointer = pointer[sn - 1];
		array[++stkPointer] = item;
		pointer[sn - 1] = stkPointer;
	}

	public void pop(int sn) throws StackException {
		if (empty(sn))
			throw new StackException(sn + " is underflow !");
		int peek = peek(sn);
		System.out.println(peek);
		pointer[sn - 1] = --pointer[sn - 1];
	}

	public int peek(int sn) throws StackException {
		authenticate(sn);
		return array[pointer[sn - 1]];
	}

	public boolean empty(int sn) throws StackException {
		authenticate(sn);
		return pointer[sn - 1] == (int)(((sn - 1) * Math.ceil(size / stackN)) - 1);
	}

	public boolean full(int sn) throws StackException {
		authenticate(sn);
		return sn == stackN ? pointer[sn - 1] == size - 1 :  pointer[sn - 1] == (int)((sn) * Math.ceil(size / stackN)) - 1;
	}

	private void authenticate(int sn) throws StackException {
		if (sn > stackN || sn < 1)
			throw new StackException("No such stack found");
	}

	public static void main(String[] args) {
		try (Scanner scanner = new Scanner(System.in)) {
			System.out.println("Define size of the stack");
			int size = scanner.nextInt();
			System.out.println("total number of stacks");
			int stackN = scanner.nextInt();
			MultipleStack stack = new MultipleStack(size, stackN);
			boolean exit = false;
			do {
				System.out.println("1. Push");
				System.out.println("2. Pop");
				System.out.println("3. Exit");
				System.out.println("Choice");
				int choice = scanner.nextInt();
				switch (choice) {
				case 1:
					try {
						System.out.println("Item : ");
						int item = scanner.nextInt();
						System.out.println("Stack Number : ");
						int stk = scanner.nextInt();
						stack.push(item, stk);
					} catch (Exception e) {
						e.printStackTrace();
					}
					break;

				case 2:
					try {
						System.out.println("Stack Number : ");
						int stk = scanner.nextInt();
						stack.pop(stk);
					} catch (Exception e) {
						e.printStackTrace();
					}
					break;

				case 3:
					exit = true;
					break;

				default:
					System.out.println("Invalid choice !");
					break;
				}
			} while (!exit);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Solution 13 - Algorithm

We can generalize it to K stacks in one Array. Basic idea is to:

> - Maintain a PriorityQueue as a min heap of currently free indexes in the allocation array. > - Maintain an array of size K, that holds the top of the stack, for each of the stacks. > - Create a Data class with 1) Value 2) Index of Prev element in the allocation array 3) Index of current element being pushed in the > allocation array > - Maintain an allocation array of type Data

Refer the code for a working sample implementation.

import java.util.*;
public class Main 
{ 
	// A Java class to represent k stacks in a single array of size n 
	public static final class KStack {

      /**
      * PriorityQueue as min heap to keep track of the next free index in the 
      * backing array.
      */
      private final PriorityQueue<Integer> minHeap = new PriorityQueue<>((a,b) -> (a - b));

      /**
      * Keeps track of the top of the stack of each of the K stacks
      */
      private final Data index[];

      /**
      * Backing array to hold the data of K stacks.
      */
      private final Data array[];
      
      public KStack(int noOfStacks, int sizeOfBackingArray) {
        index = new Data[noOfStacks];
        array = new Data[sizeOfBackingArray];
        
        for(int i =0; i< sizeOfBackingArray; i++) {
          minHeap.add(i);
        }
      }
      
      
      public void push(int val, int stackNo) {
        if(minHeap.isEmpty()) {
          return;
        }
        
        int nextFreeIdx = minHeap.poll();
        Data tos = index[stackNo];
        if(tos == null) {
          tos = new Data(val, -1 /* Previous elemnet's idx*/, nextFreeIdx
                        /* This elemnent's idx in underlying array*/);
        } else {
          tos = new Data(val, tos.myIndex, nextFreeIdx);
        }
        
        index[stackNo] = tos;
        array[nextFreeIdx] = tos;
      }
      
      public int pop(int stackNo) {
        if(minHeap.size() == array.length) {
          return -1; // Maybe throw Exception?
        }
        
        Data tos = index[stackNo];
        if(tos == null) {
          return -1; // Maybe throw Exception?
        }
        
        minHeap.add(tos.myIndex);
        array[tos.myIndex] = null;
        
        int value = tos.value;
        if(tos.prevIndex == -1) {
          tos = null;
        } else {
          tos = array[tos.prevIndex];
        }
        
        index[stackNo] = tos;
        return value;
      }
	}
  
   public static final class Data {
     int value;
     int prevIndex;
     int myIndex;
     
     public Data(int value, int prevIndex, int myIndex) {
       this.value = value;
       this.prevIndex = prevIndex;
       this.myIndex = myIndex;
     }

     @Override
     public String toString() {
       return "Value: " + this.value + ", prev: " + this.prevIndex + ", myIndex: " + myIndex; 
     }
   }

	// Driver program 
	public static void main(String[] args) 
	{ 
		int noOfStacks = 3, sizeOfBackingArray = 10; 
		
		KStack ks = new KStack(noOfStacks, sizeOfBackingArray); 

 		// Add elements to stack number 1
		ks.push(11, 0); 
		ks.push(9, 0); 
		ks.push(7, 0); 

    // Add elements to stack number 3
		ks.push(51, 2); 
		ks.push(54, 2); 

		// Add elements to stack number 2
		ks.push(71, 1); 
		ks.push(94, 1); 
		ks.push(93, 1); 


		System.out.println("Popped from stack 3: " + ks.pop(2)); 
        System.out.println("Popped from stack 3: " + ks.pop(2)); 
        System.out.println("Popped from stack 3: " + ks.pop(2)); 
		System.out.println("Popped from stack 2: " + ks.pop(1)); 
		System.out.println("Popped from stack 1: " + ks.pop(0)); 
	} 
} 

Solution 14 - Algorithm

Dr. belisarius's answer explains the basic algorithm, but doesn't go in the details, and as we know, the devil is always in the details. I coded up a solution in Python 3, with some explanation and a unit test. All operations run in constant time, as they should for a stack.

# One obvious solution is given array size n, divide up n into 3 parts, and allocate floor(n / 3) cells
# to two stacks at either end of the array, and remaining cells to the one in the middle. This strategy is not
# space efficient because even though there may be space in the array, one of the stack may overflow.
#
# A better approach is to have two stacks at either end of the array, the left one growing on the right, and the
# right one growing on the left. The middle one starts at index floor(n / 2), and grows at both ends. When the
# middle stack size is even, it grows on the right, and when it's odd, it grows on the left. This way, the middle
# stack grows evenly and minimizes the changes of overflowing one of the stack at either end.
#
# The rest is pointer arithmetic, adjusting tops of the stacks on push and pop operations.

class ThreeStacks:
    def __init__(self, n: int):
        self._arr: List[int] = [0] * n
        self._tops: List[int] = [-1, n, n // 2]
        self._sizes: List[int] = [0] * 3
        self._n = n

    def _is_stack_3_even_size(self):
        return self._sizes[2] % 2 == 0

    def _is_stack_3_odd_size(self):
        return not self._is_stack_3_even_size()

    def is_empty(self, stack_number: int) -> bool:
        return self._sizes[stack_number] == 0

    def is_full(self, stack_number: int) -> bool:
        if stack_number == 0 and self._is_stack_3_odd_size():
            return self._tops[stack_number] == self._tops[2] - self._sizes[2]
        elif stack_number == 1 and self._is_stack_3_even_size():
            return self._tops[stack_number] == self._tops[2] + self._sizes[2]

        return (self._is_stack_3_odd_size() and self._tops[0] == self._tops[2] - self._sizes[2]) or \
               (self._is_stack_3_even_size() and self._tops[1] == self._tops[2] + self._sizes[2])

    def pop(self, stack_number: int) -> int:
        if self.is_empty(stack_number):
            raise RuntimeError(f"Stack : {stack_number} is empty")

        x: int = self._arr[self._tops[stack_number]]
        if stack_number == 0:
            self._tops[stack_number] -= 1
        elif stack_number == 1:
            self._tops[stack_number] += 1
        else:
            if self._is_stack_3_even_size():
                self._tops[stack_number] += (self._sizes[stack_number] - 1)
            else:
                self._tops[stack_number] -= (self._sizes[stack_number] - 1)

        self._sizes[stack_number] -= 1
        return x

    def push(self, item: int, stack_number: int) -> None:
        if self.is_full(stack_number):
            raise RuntimeError(f"Stack: {stack_number} is full")

        if stack_number == 0:
            self._tops[stack_number] += 1
        elif stack_number == 1:
            self._tops[stack_number] -= 1
        else:
            if self._is_stack_3_even_size():
                self._tops[stack_number] += self._sizes[stack_number]
            else:
                self._tops[stack_number] -= self._sizes[stack_number]
        self._arr[self._tops[stack_number]] = item
        self._sizes[stack_number] += 1

    def __repr__(self):
        return str(self._arr)

Test:

def test_stack(self):
    stack = ThreeStacks(10)

    for i in range(3):
        with pytest.raises(RuntimeError):
            stack.pop(i)

    for i in range(1, 4):
        stack.push(i, 0)

    for i in range(4, 7):
        stack.push(i, 1)

    for i in range(7, 11):
        stack.push(i, 2)

    for i in range(3):
        with pytest.raises(RuntimeError):
            stack.push(1, i)

    assert [stack.pop(i) for i in range(3)] == [3, 6, 10]
    assert [stack.pop(i) for i in range(3)] == [2, 5, 9]
    assert [stack.pop(i) for i in range(3)] == [1, 4, 8]

    for i in range(2):
        assert stack.is_empty(i)

    assert not stack.is_empty(2)
    assert stack.pop(2) == 7
    assert stack.is_empty(2)

Solution 15 - Algorithm

> This is a very common interview question "Implement 3 stacks using a single Array or >List". >Here is my solution- > > Approach 1- Go for a fixed division of an array means if we divide our array into 3 equal parts and push the elements of an array > into three fixed-sized stacks. > For stack 1, use [0,n/3] > For stack 2, use [n/3,2n/3] > For stack 3, use [2n/3,n]. The problem with this approach is that we may face a condition where the size of an array may be > greater than the size of the stack ie. Stack Overflow condition. So, > we must take care of special cases and edge cases like this. now go > for 2nd approach. > > Approach 2- Flexible Division, In the first approach we face a condition where the size of an array may be greater than the size of > the stack ie the Stack overflow condition. we can overcome this > problem by doing a flexible division of the stack. while adding > elements to the stack, when one stack exceeds the initial capacity, > shift the elements to the next stack. So, this way we can approach > this problem.

Solution 16 - Algorithm

Here's my solution for it in C# -

/*	Program: Implement 3 stacks using a single array
 *
 *	Date: 12/26/2015
 */

using System;

namespace CrackingTheCodingInterview
{
    internal class Item
    {
        public object data;
        public int prev;
    }

    /// <summary>
    /// Class implementing 3 stacks using single array
    /// </summary>
    public class Stacks
    {
        /// <summary>
        /// Pushing an element 'data' onto a stack 'i'
        /// </summary>
        public void Push(int i, object d)
        {
            i--;
            if (available != null)
            {
                int ava = (int)available.DeleteHead();
                elems[ava].data = d;
                elems[ava].prev = top[i];
                top[i] = ava;
            }
            else
            {
                Console.WriteLine("Array full. No more space to enter!");
                return;
            }
        }

        /// <summary>
        /// Popping an element from stack 'i'
        /// </summary>
        public object Pop(int i)
        {
            i--;
            if (top[i] != -1)
            {
                object popVal = elems[top[i]].data;
                int prevTop = elems[top[i]].prev;
                elems[top[i]].data = null;
                elems[top[i]].prev = -1;
                available.Insert(top[i]);
                top[i] = prevTop;

                return popVal;
            }
            else
            {
                Console.WriteLine("Stack: {0} empty!", i);
                return null;
            }
        }

        /// <summary>
        /// Peeking top element of a stack
        /// </summary>
        public object Peek(int i)
        {
            i--;
            if (top[i] != -1)
            {
                return elems[top[i]].data;
            }
            else
            {
                Console.WriteLine("Stack: {0} empty!", i);
                return null;
            }
        }

        /// <summary>
        /// Constructor initializing array of Nodes of size 'n' and the ability to store 'k' stacks
        /// </summary>
        public Stacks(int n, int k)
        {
            elems = new Item[n];
            top = new int[k];

            for (int i = 0; i < k; i++)
            {
                top[i] = -1;
            }

            for (int i = 0; i < n; i++)
            {
                elems[i] = new Item();
                elems[i].data = null;
                elems[i].prev = -1;
            }

            available = new SinglyLinkedList();

            for (int i = n - 1; i >= 0; i--)
            {
                available.Insert(i);
            }
        }

        private Item[] elems;
        private int[] top;
        private SinglyLinkedList available;
    }

    internal class StacksArrayTest
    {
        static void Main()
        {
            Stacks s = new Stacks(10, 3);
            s.Push(1, 'a');
            s.Push(1, 'b');
            s.Push(1, 'c');

            Console.WriteLine("After pushing in stack 1");
            Console.WriteLine("Top 1: {0}", s.Peek(1));

            s.Push(2, 'd');
            s.Push(2, 'e');
            s.Push(2, 'f');
            s.Push(2, 'g');

            Console.WriteLine("After pushing in stack 2");
            Console.WriteLine("Top 1: {0}", s.Peek(1));
            Console.WriteLine("Top 2: {0}", s.Peek(2));

            s.Pop(1);
            s.Pop(2);

            Console.WriteLine("After popping from stack 1 and 2");
            Console.WriteLine("Top 1: {0}", s.Peek(1));
            Console.WriteLine("Top 2: {0}", s.Peek(2));

            s.Push(3, 'h');
            s.Push(3, 'i');
            s.Push(3, 'j');
            s.Push(3, 'k');
            s.Push(3, 'l');

            Console.WriteLine("After pushing in stack 3");
            Console.WriteLine("Top 3: {0}", s.Peek(3));

            Console.ReadLine();
        }
    }
}

Output:

After pushing in stack 1
Top 1: c
After pushing in stack 2
Top 1: c
Top 2: g
After popping from stack 1 and 2
Top 1: b
Top 2: f
After pushing in stack 3
Top 3: l

I refer to this post for coding it - http://codercareer.blogspot.com/2013/02/no-39-stacks-sharing-array.html

Solution 17 - Algorithm

package job.interview; import java.util.Arrays;

public class NStack1ArrayGen<T> {

	T storage[];
	int numOfStacks;
	Integer top[];
	public NStack1ArrayGen(int numOfStks, T myStorage[]){
		storage = myStorage;
		numOfStacks = numOfStks;
		top = new Integer[numOfStks];
		for(int i=0;i<numOfStks;i++){top[i]=-1;}
	}
	public void push(int stk_indx, T value){
		int r_indx = stk_indx -1;
		if(top[r_indx]+numOfStacks < storage.length){
			top[r_indx] = top[r_indx] < 0 ? stk_indx-1 : top[r_indx]+numOfStacks;
			storage[top[r_indx]] = value;
		}
	}
	public T pop(int stk_indx){
		T ret = top[stk_indx-1]<0 ? null : storage[top[stk_indx-1]];
		top[stk_indx-1] -= numOfStacks;
		return ret;
	}
	public void printInfo(){
		print("The array", Arrays.toString(storage));
		print("The top indices", Arrays.toString(top));
		for(int j=1;j<=numOfStacks;j++){
			printStack(j);
		}
	}
	public void print(String name, String value){
		System.out.println(name + " ==> " + value);
	}
	public void printStack(int indx){
		String str = "";
		while(top[indx-1]>=0){
			str+=(str.length()>0 ? "," : "") + pop(indx);
		}
		print("Stack#"+indx,str);
	}
	public static void main (String args[])throws Exception{
		int count=4, tsize=40;
		int size[]={105,108,310,105};
		NStack1ArrayGen<String> mystack = new NStack1ArrayGen<String>(count,new String[tsize]); 
        for(int i=1;i<=count;i++){
        	for(int j=1;j<=size[i-1];j++){
        		mystack.push(i, "stk"+i+"_value"+j);
        	}
        }
    }
}

This prints:

>The array ==> [stk1_value1, stk2_value1, stk3_value1, stk4_value1, stk1_value2, stk2_value2, stk3_value2, stk4_value2, stk1_value3, stk2_value3, stk3_value3, stk4_value3, stk1_value4, stk2_value4, stk3_value4, stk4_value4, stk1_value5, stk2_value5, stk3_value5, stk4_value5, stk1_value6, stk2_value6, stk3_value6, stk4_value6, stk1_value7, stk2_value7, stk3_value7, stk4_value7, stk1_value8, stk2_value8, stk3_value8, stk4_value8, stk1_value9, stk2_value9, stk3_value9, stk4_value9, stk1_value10, stk2_value10, stk3_value10, stk4_value10] The top indices ==> [36, 37, 38, 39] Stack#1 ==> stk1_value10,stk1_value9,stk1_value8,stk1_value7,stk1_value6,stk1_value5,stk1_value4,stk1_value3,stk1_value2,stk1_value1 Stack#2 ==> stk2_value10,stk2_value9,stk2_value8,stk2_value7,stk2_value6,stk2_value5,stk2_value4,stk2_value3,stk2_value2,stk2_value1 Stack#3 ==> stk3_value10,stk3_value9,stk3_value8,stk3_value7,stk3_value6,stk3_value5,stk3_value4,stk3_value3,stk3_value2,stk3_value1 Stack#4 ==> stk4_value10,stk4_value9,stk4_value8,stk4_value7,stk4_value6,stk4_value5,stk4_value4,stk4_value3,stk4_value2,stk4_value1

Solution 18 - Algorithm

enum stackId{LEFT, MID, RIGHT };

class threeStacks {

int* arr;

int leftSize;
int rightSize;
int midSize;
int mid;
int maxSize;
public:
	threeStacks(int n):leftSize(0), rightSize(0), midSize(0), mid(n/2), maxSize(n)
	{
		arr = new int[n];
	}
	
	void push(stackId sid, int val){
		switch(sid){
			case LEFT:
				pushLeft(val);
			break;
			
			case MID:
				pushMid(val);
			break;
			
			case RIGHT:
				pushRight(val);
		}
	}
	
	int pop(stackId sid){
		switch(sid){
			case LEFT:
				return popLeft();
			case MID:
				return popMid();
			case RIGHT:
				return popRight();
		}
	}
	
	
	int top(stackId sid){
		switch(sid){
			case LEFT:
				return topLeft();
			case MID:
				return topMid();
			case RIGHT:
				return topRight();
		}
	}
	
	void pushMid(int val){
		if(midSize+leftSize+rightSize+1 > maxSize){
			cout << "Overflow!!"<<endl;
			return;
		}
		if(midSize % 2 == 0){
			if(mid - ((midSize+1)/2) == leftSize-1){
				//left side OverFlow
				if(!shiftMid(RIGHT)){
					cout << "Overflow!!"<<endl;
					return;	
				}
			}
			midSize++;
			arr[mid - (midSize/2)] = val;
		}
		else{
			if(mid + ((midSize+1)/2) == (maxSize - rightSize)){
				//right side OverFlow
				if(!shiftMid(LEFT)){
					cout << "Overflow!!"<<endl;
					return;	
				}
			}
			midSize++;
			arr[mid + (midSize/2)] = val;							
		}
	}
	
	int popMid(){
		if(midSize == 0){
			cout << "Mid Stack Underflow!!"<<endl;
			return -1;
		}
		int val;
		if(midSize % 2 == 0)
			val = arr[mid - (midSize/2)];
		else
			val = arr[mid + (midSize/2)];
		midSize--;
		return val;
	}
	
	int topMid(){
		if(midSize == 0){
			cout << "Mid Stack Underflow!!"<<endl;
			return -1;
		}
		int val;
		if(midSize % 2 == 0)
			val = arr[mid - (midSize/2)];
		else
			val = arr[mid + (midSize/2)];
		return val;
	}
	
	bool shiftMid(stackId dir){
		int freeSpace;
		switch (dir){
			case LEFT:
				freeSpace = (mid - midSize/2) - leftSize;
				if(freeSpace < 1)
					return false;				
				if(freeSpace > 1)
					freeSpace /= 2;
				for(int i=0; i< midSize; i++){
					arr[(mid - midSize/2) - freeSpace + i] = arr[(mid - midSize/2) + i];
				}
				mid = mid-freeSpace;
			break;
			case RIGHT:
				freeSpace = maxSize - rightSize - (mid + midSize/2) - 1;
				if(freeSpace < 1)
					return false;				
				if(freeSpace > 1)
					freeSpace /= 2;
				for(int i=0; i< midSize; i++){
					arr[(mid + midSize/2) + freeSpace - i] = arr[(mid + midSize/2) - i];
				}
				mid = mid+freeSpace;
			break;				
			default:
				return false;
		}
	}
	
	void pushLeft(int val){
		if(midSize+leftSize+rightSize+1 > maxSize){
			cout << "Overflow!!"<<endl;
			return;
		}
		if(leftSize == (mid - midSize/2)){
			//left side OverFlow
			if(!shiftMid(RIGHT)){
				cout << "Overflow!!"<<endl;
				return;	
			}
		}
		arr[leftSize] = val;
		leftSize++;
	}
	
	int popLeft(){
		if(leftSize == 0){
			cout << "Left Stack Underflow!!"<<endl;
			return -1;
		}
		leftSize--;
		return arr[leftSize];
	}
	
	int topLeft(){
		if(leftSize == 0){
			cout << "Left Stack Underflow!!"<<endl;
			return -1;
		}
		return arr[leftSize - 1];
	}
	
	void pushRight(int val){
		if(midSize+leftSize+rightSize+1 > maxSize){
			cout << "Overflow!!"<<endl;
			return;
		}
		if(maxSize - rightSize - 1 == (mid + midSize/2)){
			//right side OverFlow
			if(!shiftMid(LEFT)){
				cout << "Overflow!!"<<endl;
				return;	
			}
		}
		rightSize++;
		arr[maxSize - rightSize] = val;
	}
	
	int popRight(){
		if(rightSize == 0){
			cout << "Right Stack Underflow!!"<<endl;
			return -1;
		}
		int val = arr[maxSize - rightSize];
		rightSize--;
		return val;
	}
	
	int topRight(){
		if(rightSize == 0){
			cout << "Right Stack Underflow!!"<<endl;
			return -1;
		}
		return arr[maxSize - rightSize];
	}

};

Solution 19 - Algorithm

Python

class Stack:
    
    def __init__(self):
        self.pos_1 = 0
        self.pos_2 = 1
        self.pos_3 = 2
        self.stack = [None, None, None]
        
    def pop_1(self):
        if self.pos_2 - 1 > 0:
            to_ret = self.stack.pop(self.pos_1)
            self.pos_2 -= 1
            self.pos_3 -= 1
        return to_ret
    
    def push_1(self, value):
        self.stack.insert(self.pos_1, value)
        self.pos_2 += 1
        self.pos_3 += 1
        return None
    
    def pop_2(self):
        if self.pos_2 - 1 < self.pos_3:
            to_ret = self.stack.pop(self.pos_2)
            self.pos_3 -= 1
        return to_ret
    
    def push_2(self, value):
        self.stack.insert(self.pos_2, value)
        self.pos_3 += 1
        return None
    
    def pop_3(self):
        if self.pos_3 - 1 > self.pos_2:
            to_ret = self.stack.pop(self.pos_3)
        return to_ret
    
    def push_3(self, value):
        self.stack.insert(self.pos_3, value)
        return None
    
if __name__ == "__main__":
    stack = Stack()
    stack.push_2(22)
    stack.push_1(1)
    stack.push_1(2)
    print stack.pop_1()
    print stack.pop_1()
    print stack.pop_2()

prints: 2 1 22

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMichaelView Question on Stackoverflow
Solution 1 - AlgorithmDr. belisariusView Answer on Stackoverflow
Solution 2 - Algorithmstakx - no longer contributingView Answer on Stackoverflow
Solution 3 - AlgorithmhughdbrownView Answer on Stackoverflow
Solution 4 - AlgorithmpunkWizardView Answer on Stackoverflow
Solution 5 - AlgorithmSteve JessopView Answer on Stackoverflow
Solution 6 - AlgorithmpayneView Answer on Stackoverflow
Solution 7 - AlgorithmjbappleView Answer on Stackoverflow
Solution 8 - AlgorithmkishoreView Answer on Stackoverflow
Solution 9 - AlgorithmOleksiy RazoryonovView Answer on Stackoverflow
Solution 10 - AlgorithmPratul KumarView Answer on Stackoverflow
Solution 11 - AlgorithmMohit SinghView Answer on Stackoverflow
Solution 12 - AlgorithmVpn_talentView Answer on Stackoverflow
Solution 13 - AlgorithmAnupam SainiView Answer on Stackoverflow
Solution 14 - AlgorithmAbhijit SarkarView Answer on Stackoverflow
Solution 15 - AlgorithmAkshay KumarView Answer on Stackoverflow
Solution 16 - AlgorithmAnkitView Answer on Stackoverflow
Solution 17 - AlgorithmMeelirView Answer on Stackoverflow
Solution 18 - AlgorithmscoopedOutFlatBagelView Answer on Stackoverflow
Solution 19 - AlgorithmCodifyView Answer on Stackoverflow