How to implement 3 stacks with one array?

后端 未结 19 2262
迷失自我
迷失自我 2021-01-29 18:44

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.

相关标签:
19条回答
  • 2021-01-29 19:01

    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.

    0 讨论(0)
  • 2021-01-29 19:03

    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.

    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

    0 讨论(0)
  • 2021-01-29 19:04

    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();
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-29 19:05

    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)); 
        } 
    } 
    
    0 讨论(0)
  • 2021-01-29 19:06

    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()
    
    0 讨论(0)
  • 2021-01-29 19:07

    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.

    0 讨论(0)
提交回复
热议问题