Stack - Institution and Implementation
Stack - Institution and Implementation
IMPLEMENTATION
16 February 2024 17:21
What is Stack ?
A stack is a linear data structure in which the insertion of a new element and removal of an
existing element takes place at the same end represented as the top of the stack.
To implement the stack, it is required to maintain the pointer to the top of the stack, which
is the last element to be inserted because we can access the elements only on the top of
the stack.
begin
if stack is full
return
endif
else
increment top
stack[top] assign value
end else
end procedure
Pop:
Removes an item from the stack. The items are popped in the reversed order in which they
are pushed. If the stack is empty, then it is said to be an Underflow condition.
begin
if stack is empty
return
endif
else
store value of stack[top]
decrement top
return value
end else
end procedure
Top:
isEmpty:
There are many real-life examples of a stack. Consider the simple example of plates stacked
over one another in a canteen. The plate which is at the top is the first one to be removed,
i.e. the plate which has been placed at the bottommost position remains in the stack for the
longest period of time. So, it can be simply seen to follow the LIFO/FILO order.
Complexity Analysis:
• Time Complexity
Operations Complexity
push() O(1)
pop() O(1)
isEmpty() O(1)
size() O(1)
Types of Stacks:
• Fixed Size Stack: As the name suggests, a fixed size stack has a fixed size and cannot
grow or shrink dynamically. If the stack is full and an attempt is made to add an
element to it, an overflow error occurs. If the stack is empty and an attempt is made
to remove an element from it, an underflow error occurs.
• Dynamic Size Stack: A dynamic size stack can grow or shrink dynamically. When the
stack is full, it automatically increases its size to accommodate the new element, and
when the stack is empty, it decreases its size. This type of stack is implemented using a
linked list, as it allows for easy resizing of the stack.
In addition to these two main types, there are several other variations of Stacks, including :
1. Infix to Postfix Stack: This type of stack is used to convert infix expressions to postfix
expressions.
2. Expression Evaluation Stack: This type of stack is used to evaluate postfix
expressions.
3. Recursion Stack: This type of stack is used to keep track of function calls in a computer
program and to return control to the correct function when a function returns.
4. Memory Management Stack: This type of stack is used to store the values of the
program counter and the values of the registers in a computer program, allowing the
program to return to the previous state when a function returns.
5. Balanced Parenthesis Stack: This type of stack is used to check the balance of
parentheses in an expression.
6. Undo-Redo Stack: This type of stack is used in computer programs to allow users to
undo and redo actions.
Implementation of Stack :
The basic operations that can be performed on a stack include push, pop, and peek. There
are two ways to implement a stack –
• Using array
• Using linked list
In an array-based implementation, the push operation is implemented by incrementing the
index of the top element and storing the new element at that index. The pop operation is
implemented by decrementing the index of the top element and returning the value stored
at that index.
In a linked list-based implementation, the push operation is implemented by creating a new
node with the new element and setting the next pointer of the current top node to the new
node. The pop operation is implemented by setting the next pointer of the current top node
to the next node and returning the value of the current top node.
Example 1:
Input
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
Output
[null, null, null, 2, 2, false]
Explanation
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // return 2
myStack.pop(); // return 2
myStack.empty(); // return False
Constraints:
• 1 <= x <= 9
• At most 100 calls will be made to push, pop, top, and empty.
• All the calls to pop and top are valid.
Problem Understanding
Description :
The task is to implement a stack (Last In, First Out - LIFO) using queues. The
stack should support the following functions: push, pop, top, and empty. These
operations should behave just as they do in a typical stack data structure.
Two-Queue Approach :
• How it Works: Utilize two queues and move elements between them to
mimic stack operations.
• Time Complexity: O(n)O(n)O(n) for pop and top, O(1)O(1)O(1) for push and
empty.
• When to Use: Choose this approach when the frequency of push and empty
operations is higher than pop and top.
One-Queue Approach :
• How it Works: Use a single queue and reorganize (rotate) its elements
when pushing a new element.
• Time Complexity: O(n)O(n)O(n) for push, O(1)O(1)O(1) for pop, top, and
empty.
• When to Use: Choose this approach when you expect pop and top
operations to be more frequent than push.
3. Thought Process
Conclusion
The problem is a good exercise in understanding how one data structure can be
implemented using another while maintaining its essential characteristics with
both one-queue and two-queue approaches.
• Two queues, often implemented using Python's deque from the collections
library.
Enhanced Breakdown
1. Initialization:
• Create two empty queues, referred to as q1 and q2. q1 will serve as the
main queue where new elements are pushed, while q2 will serve as an
auxiliary queue for temporary operations.
2. Base Cases:
• For the empty function, directly check the length or size of q1. If it's
empty, return True, otherwise return False.
3. Main Algorithm:
Push Operation
• For the push operation, simply add the new element to the back (enqueue)
of q1.
Pop Operation
• For the pop operation, you need to access the last element pushed into q1,
which is at the back.
• To do this, dequeue from q1 and enqueue into q2 until only one element
remains in q1.
• The remaining element is the one to be popped. Dequeue it from q1.
Top Operation
• Similar to the pop operation, but after accessing the last element, it
should be enqueued back into q2.
4. Wrap-up:
• After each pop and top operation, swap q1 and q2 to make q2 the new main
queue and q1 the new auxiliary queue.
Complexity
Time Complexity :
• O(n)O(n)O(n) for the pop and top operations as you have to move n−1n-1n−1
elements between the two queues.
• O(1)O(1)O(1) for the push and empty operations as they are direct enqueue
and length check operations.
Space Complexity :
• A single queue, again best implemented using Python's deque from the
collections library.
Enhanced Breakdown
1. Initialization:
• Create a single empty queue, referred to as q.
2. Base Cases:
• Similar to the Two-Queue Approach, directly check if the queue q is empty
for the empty operation.
3. Main Algorithm :
Push Operation
• Enqueue the new element at the back of q.
• To ensure that the last element can be accessed from the front, rotate
the queue by dequeueing and enqueuing each element except the newly
pushed element.
Complexity
Time Complexity:
• O(n)O(n)O(n) for the push operation due to the rotation required to bring
the last element to the front.
• O(1)O(1)O(1) for the pop, top, and empty operations.
Space Complexity:
Performance :
Design a stack that supports push, pop, top, and retrieving the
minimum element in constant time.
You must implement a solution with O(1) time complexity for each
function.
Example 1:
Input
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
Output
[null,null,null,null,-3,null,0,-2]
Explanation
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); // return -3
minStack.pop();
minStack.top(); // return 0
minStack.getMin(); // return -2
Constraints:
Solution:
Solution 1 : Using pairs to store the value and minimum element till now.
Approach :
The first element in the pair will store the value and the second element
will store the minimum element till now.
When the first push operation comes in we will push the value and store it
as minimum itself in the pair.
In the second push operation, we will check if the top element’s minimum
is less than the new value. If it is then we will push the value with
minimum as the previous top’s minimum. To get the getMin element to
take the top’s second element.
Code :
Approach :
Let’s take a variable that stores the minimum number. So whenever a push
operation comes in just take that number put it in the stack and update
the variable to the number.
Push operation:
Now if there is a push operation just check whether that number is less
than the min number. If it is smaller than min we will push a modified
value which is a push(2 * Val – min) into the stack and will update min to
the value of the original number. If it’s not then we will just push it as it
is.
getMin() operation :
Top operation :
While returning the top value we know that it is a modified value. We will
check if the top value is lesser than min, If it is then we will return the
min as the top value.
Pop operation :
While making pop we will check if the top value is lesser than min, If it is
then we must update our min to its previous value. In order to do that min
= (2 * min) – (modified value) and we will pop the element.
Code :