Amazon interview: Min stack - algorithm

I recently had my Amazon interview for SDE. I was asked to design a stack which does push, pop and min in O(1).
I got the logic and implemented the push of the stack. While implementing push of the new stack, I called push on given stack and min stack which were a part of new stack. The interviewer told me that i couldn't do it like that as push would be a recursive call. I explained to him, that we could name it differently, but he insisted that both operations on old stack and new stack be called push.
How could I achieve the same?

One way to implement this is to keep track of the minimum of all values that are below some element in the stack.
For every element in the stack you will actually have 2 elements. One with the actual value, and above it, the minimum value of all the elements underneath it.
Push - Compare the new value with the top minimum and push both the value and the current minimum.
Pop - Just pop twice from the stack (both the value and the current minimum).
Min - Return the top of the stack.
Example: For elements 7, 9, 3, 5, 1, 2 (in this order) the stack will be:
TOP: 1 <--- min(7,9,3,5,1,2)
2
1 <--- min(7,9,3,5,1)
1
3 <--- min(7,9,3,5)
5
3 <--- min(7,9,3)
3
7 <--- min(7,9)
9
7 <--- min(7)
7

The solution is simple and elegant — Use an extra stack to maintain the minimums.
1.To retrieve the current minimum, just return the top element from minimum stack.
2.Each time you perform a push operation, check if the pushed element is a new minimum. If it is, push it to the minimum stack
too.
3.When you perform a pop operation, check if the popped element is the same as the current minimum. If it is, pop it off the minimum
stack too.

Make each value in a regular stack a vector with 2 elements, where the first element would represent a value and a second one a running minimum.
Wrapping methods could mask a vector, so only a regular value would be available in the interfaces and signatures.

Related

What is stack in data structure and how it is different from queue? [duplicate]

This question already has answers here:
what is the basic difference between stack and queue?
(12 answers)
Closed 1 year ago.
What is stack in data structure? What is importance of using stack over queue? I created stack in C but cannot understand it's principle and benefits.
Stack : A stack is a linear data structure in which elements can be inserted and deleted only from one side of the list, called the top. A stack follows the LIFO (Last In First Out) principle, i.e., the element inserted at the last is the first element to come out. The insertion of an element into stack is called push operation, and deletion of an element from the stack is called pop operation. In stack we always keep track of the last element present in the list with a pointer called top.
Queue: A queue is a linear data structure in which elements can be inserted only from one side of the list called rear, and the elements can be deleted only from the other side called the front. The queue data structure follows the FIFO (First In First Out) principle, i.e. the element inserted at first in the list, is the first element to be removed from the list. The insertion of an element in a queue is called an enqueue operation and the deletion of an element is called a dequeue operation. In queue we always maintain two pointers, one pointing to the element which was inserted at the first and still present in the list with the front pointer and the second pointer pointing to the element inserted at the last with the rear pointer.

Find the maximum and a minimum in a stack

Suppose there are 50000+ integer entries in a Stack? How to efficiently find out a maximum or minimum in it?
Stack can grow or reduce i.e., pop() and push(), but we need to keep track of max and min value in the stack?
As Mischel pointed out, Stack with find-min/find-max more efficient than O(n)?, already answers the question.
However that response suggests that you need to store each and every max and min at each point in the stack. A better solution would be to have a separate stack for the max and the min. For the max stack you will push a new element onto the max stack only if the new element is greater than the current max, and vice-versa for min. You will pop elements of the min and max stacks when the element that you are popping of the main stack is equal to them and not equal to the next element in the main stack.
Note that this requires O(n) extra space while providing O(1) operations.
The only idea I have is to inherit Stack and keep inside max and min. On pop and push do changes to your max and min.

Is there any priority queue with O(1) insertion and removal when the elements are added in ascending order?

It is not possible to make a priority queue with O(1) insertion and removal. After all, if it was possible, then one could sort a list in O(N). But, suppose that the elements are added in ascending order - that is, in a particular use instance, the last element added is always the first element to be removed. Then, is there a priority queue that will behave with constant lookup/removal for that use?
Note: I am not asking for a stack. I'm asking for a priority queue that degenerates to constant lookup/removals under this specific usage pattern - similar to sorting algorithms that degenerate to O(N) best case for almost-sorted lists.
Use a linked list sorted in descending priority order.
For removing an element, just remove the first element. This is always O(1).
For adding an element:
If the list is empty, simply insert the element.
Otherwise, walk the list until a lower-priority element is found. Insert the new element before the lower-priority element.
This is O(1) in the case that the new element is the highest priority. This algorithm will check the first element, and either find it to not exist (in the case of the empty list) or be a lower priority than the new element.
There's certainly nothing stopping you from implementing such a beast.
For example, when you add an element to your currently empty collection, or when the items are still coming in in increasing order, you simply add the item to a stack.
However, if at any point you add an item with priority less than the previous one, simply move all the stack entries across to an empty queue (in all mentions of queue below, I mean priority queue) under the covers, before finally adding your new item to that queue.
Once you empty out the queue, you will again revert to the stack as default.
So, basically, the pseudo-code would be something like this. First, the instance variables:
class uberqueue:
lastPrio = 0
stack = []
prioq = []
Then the put function which performs the actions specified above:
def put (item):
# Empty, use stack.
if stack.size() == 0 and prioq.size() == 0:
stack.push(item)
lastPrio = item.prio
return
# Stacking and in order, add to stack.
if stack.size() > 0 and item.prio >= lastPrio:
stack.push(item)
lastPrio = item.prio
return
# Stacking and out of order, transfer stack to queue.
while stack.size() > 0:
prioq.put(stock.pop())
# Queueing, add to queue.
prioq.put(item)
And, finally, the get function which just gets an item from the currently active underlying structure (stack or queue):
def get():
# Just get from whatever underlying structure is in use.
if stack.size() > 0:
return stack.pop()
return prioq.get()
And, there you have it, a data structure that gives you O(1) insert/retrieve performance if you follow the priority sequencing guidelines, but reverts to priority queue performance otherwise.
And, in fact, you could probably even get rid of the stack-to-queue transfer totally if both stack and queue offered the ability to peek at the next item priority.
A way of doing that would be to place items into the stack when their priority was greater than or equal to that of the top element. That ensures all items in the stack are ordered correctly. All out-of-order items go straight into the priority queue.
The get functionality then has to change slightly, peeking at the stack and queue to see which has the highest priority, and extracting that one.
It's a bit trickier but may offer advantages in some circumstances.

recursion and memory usage in it

i recently saw a question which req. to reverse a stack in O(1) space.
1) stack is not necessarily of an array ... we can't access index.
2)number of elements are not known.
i came up with below code and it is working but not convinced that it is O(1) space because i have declared "int temp" exactly n times, suppose there are initially n elements in stack)so it has taken O(n) space.
please tell i am right or not and is there a better way to find the solution?.
code:
#include<bits/stdc++.h>
using namespace std;
stack<int>st;
void rec()
{
if(st.empty())
return;
int temp=st.top();
st.pop();
rec();
st.push(temp);
}
int main()
{
st.push(1);
st.push(2);
st.push(3);
st.push(4);
rec();
}
You can build 2 stacks "back to back" in a single array with n elements. Basically stack #1 is a "normal" stack, and stack #2 grows "downwards" from the end of the array.
Whenever the 2 stacks together contain all n elements, there is no gap between them, so for example popping an element from stack #1 and immediately pushing it onto stack #2 in this situation can be accomplished without even moving any data: just move the top pointer for stack #1 down, and the top pointer for stack #2 physically down (but logically up).
Suppose we start with all elements in stack #1. Now you can pop all of them except the last one, immediately pushing each onto stack #2. The last element you can pop off and store in a temporary place x (O(1) extra storage, which we are allowed). Now pop all n-1 items in stack #2, pushing each in turn back onto stack #1, and then finally push x back onto (the now-empty) stack #2. At this point, we have succeeded in deleting the bottom element in stack #1, and putting it at the top of (well, it's the only element in) stack #2.
Now just recurse: pretend we only have n-1 items, and solve this smaller problem. Keep recursing until all elements have been pushed onto stack #2 in reverse order. In one final step, pop each of them off and push them back onto stack #1.
All in all, O(n^2) steps are required, but we manage with just O(1) space.
The only way I can think of is to write your own stack using a linked list and then swap the head/tail pointers and a "direction" indicator which tells your routine to go forward or backwards when you push/pop. Any other way I can think of would be O(n).
If you know upper limit of n you can also use an array/index instead of a list.
Whether it makes sense to do so is probably dependent on the reason for doing so and the language.

quick sort implementation using stack

I am reading a quick sort implementation using a stack at the following link.
link
My question is regarding the following paragraph.
The policy of putting the larger of the small subfiles on the stack
ensures that each entry on the stack is no more than one-half of the
size of the one below it, so that the stack needs to contain room for
only about lg N entries. This maximum stack usage occurs when the
partition always falls at the center of the file. For random files,
the actual maximum stack size is much lower; for degenerate files it
is likely to be small.
This technique does not necessarily work in a truly recursive
implementation, because it depends on end- or tail-recursion removal.
If the last action of a procedure is to call another procedure, some
programming environments will arrange things such that local variables
are cleared from the stack before, rather than after, the call.
Without end-recursion removal, we cannot guarantee that the stack size
will be small for quicksort.
What does the author mean by "that each entry on the stack is no more than one-half of the size of the one below it"? Could you please give an example of this.
How did the author came to the conclusion that the stack needs space for only about lg N entries?
What does authore mean by "Without end-recursion removal, we cannot guarantee that the stack size will be small for quicksort" ?
Thanks for your time and help.
The policy of putting the larger of the small subfiles on the stack ensures that each entry on the stack is no more than one-half of the size of the one below it,
That is not quite true. Consider you want to sort a 100-element array, and the first pivot goes right in the middle. Then you have a stack
49
50
then you pop the 49-element part off the stack, partition, and push the two parts on the stack. Let's say the choice of pivot was not quite as good this time, there were 20 elements not larger than the pivot. Then you'd get the stack
20
28
50
and each stack entry is more than half of the one below.
But that cannot continue forever, and we have
During the entire sorting, if stack level k is occupied, its size is at most total_size / (2^k).
That is obviously true when the sorting begins, since then there is only one element on the stack, at level 0, which is the entire array of size total_size.
Now, assume the stated property holds on entering the loop (while(!stack.empty())).
A subarray of length s is popped from stack level m. If s <= 1, nothing else is done before the next loop iteration, and the invariant continues to hold. Otherwise, if s >= 2, After partitioning that, there are two new subarrays to be pushed on the stack, with s-1 elements together. The smaller of those two then has a size smaller_size <= (s-1)/2, and the larger has a size larger_size <= s-1. Stack level m will be occupied by the larger of the two, and we have
larger_size <= s-1 < s <= total_size / (2^m)
smaller_size <= (s-1)/2 < s/2 <= total_size / (2^(m+1))
for the stack levels m resp. m+1 at the end of the loop body. The invariant holds for the next iteration.
Since at most one subarray of size 0 is ever on the stack (it is then immediately popped off in the next iteration), there are never more than lg total_size + 1 stack levels occupied.
Regarding
What does author mean by "Without end-recursion removal, we cannot guarantee that the stack size will be small for quicksort" ?
In a recursive implementation, you can have deep recursion, and when the stack frame is not reused for the end-call, you may need linear stack space. Consider a stupid pivot selection, always choosing the first element as pivot, and an already sorted array.
[0,1,2,3,4]
partition, pivot goes in position 0, the smaller subarray is empty. The recursive call for the larger subarray [1,2,3,4], allocates a new stack frame (so there are now two stack frames). Same principle, the next recursive call with the subarray [2,3,4] allocates a third stack frame, etc.
If one has end-recursion removal, i.e. the stack frame is reused, one has the same guarantees as with the manual stack above.
I will try to answer your question (hopefully I am not wrong)...
Every step in quicksort you divide your input into two (one half). By doing so, you need logN. This explains your first and second question ("each entry on the stack is no more than one-half" and "logN" entries)

Resources