A stack is considered to be a linear data structure because it is based on the principle of Last In First Out (LIFO). In a stack, elements are added and removed from the same end, called the top of the stack. The last element added to the stack is the first one to be removed, creating a linear order of elements.
The name "stack" comes from the idea of a pile of objects, like a stack of plates in a cafeteria. In a cafeteria, plates are added to the top of the stack as they are cleaned and placed back on the shelf. When a plate is needed, it is taken from the top of the stack. The first plate that was placed on the stack will be the last one to be used.
Stack supports two primary operations: push and pop. When we add an element to the top of the stack, it is called a push operation. When we remove an element from the top of the stack, it is called a pop operation.
In addition to push and pop, several other operations are supported by stack data structures:
It is possible to implement a stack data structure using an array. To do this, we can define a class called StackArray that provides access to a set of methods that implement stack operations. Users can call these methods from outside the class using an object of the StackArray class. This is an example of encapsulation and abstraction, as we are hiding the implementation details of the stack from the outside world and only providing access to the operations that can be performed on it.
We declare three private attributes inside the StackArray class:
The StackArray class has several public member methods:
StackArray(int size): this is the constructor for the StackArray class. It takes an integer size as an argument, which represents the capacity of the stack. The constructor initializes the stack array, sets the capacity to size, and sets the top to -1 to indicate that the stack is empty.
StackArray(int size)
{
Stack = new int[size];
capacity = size;
top = -1;
}
void push(int x): this method adds an element x onto the top of the stack. If the stack is full, it prints an error message and returns. Otherwise, it increments top by 1 and places the element at the top of the stack.
void push(int x)
{
if (isFull() == true)
{
cout << "Stack is full! Cannot add " << x << " to stack." << endl;
return;
}
top = top + 1;
Stack[top] = x;
}
int pop(): this method removes the top element from the stack and returns it. If the stack is empty, it prints an error message and returns INT_MIN.
int pop()
{
if (isEmpty() == true)
{
cout << "Stack is empty! Cannot pop from stack." << endl;
return INT_MIN;
}
int temp = Stack[top];
top = top - 1;
return temp;
}
int peek(): this method returns the top element of the stack without removing it. If the stack is empty, it prints an error message and returns INT_MIN.
int peek()
{
if (isEmpty() == true)
{
cout << "Stack is empty!" << endl;
return INT_MIN;
}
return Stack[top];
}
int size(): this method returns the number of elements in the stack.
bool isEmpty(): this method returns true if the stack is empty, and false otherwise.
bool isFull(): this method returns true if the stack is full, and false otherwise.
int size()
{
return top + 1;
}
bool isEmpty()
{
if(top == -1)
return true;
else
return false
}
bool isFull()
{
if(top == capacity - 1)
return true
else
return false
}
class StackArray
{
private:
int *Stack;
int top;
int capacity;
public:
StackArray(int size)
{
Stack = new int[size];
capacity = size;
top = -1;
}
void push(int x)
{
if (isFull())
{
cout << "Stack is full! Cannot add " << x << " to stack." << endl;
return;
}
top = top + 1;
Stack[top] = x;
}
int pop()
{
if (isEmpty())
{
cout << "Stack is empty! Cannot pop from stack." << endl;
return INT_MIN;
}
int temp = Stack[top];
top = top - 1;
return temp;
}
int peek()
{
if (isEmpty())
{
cout << "Stack is empty!" << endl;
return INT_MIN;
}
return Stack[top];
}
int size()
{
return top + 1;
}
bool isEmpty()
{
return top == -1;
}
bool isFull()
{
return top == capacity - 1;
}
};
One advantage of using an array to implement a stack is that it is relatively simple to set up. Additionally, because it does not require the use of pointers, it can be more memory-efficient. However, there are also some downsides to this approach. For example, the stack is not dynamic, so it cannot change its size at runtime to meet changing needs. This means that the total size of the stack must be defined beforehand.
The array implementation of a stack has a fixed size, which can be a critical limitation. To address this issue, we can use a dynamic array instead. When the stack is full, we can increase its capacity by resizing the array to double its original size.
This is a StackDynamicArray class that implements a stack data structure using an array with dynamic resizing. In this class, we have added resizeStack() method and modified void push(int x) method.
void resizeStack(): this is a private method that increases the capacity of the stack array by a factor of 2. It creates a new array called temp with 2 * capacity elements, copies the elements from stack to temp, and then deletes the stack array. Finally, it sets the stack array to temp and updates the capacity variable.
void resizeStack()
{
int *temp = new int[2 * capacity];
for (int i = 0; i < capacity; i = i + 1)
temp[i] = stack[i];
delete [] stack;
stack = temp;
capacity = 2 * capacity;
}
void push(int x): this method add an element x onto the stack. If the stack is full, it calls the resizeStack() function to increase the capacity of the stack. Then, it increments top by 1 and places the element at the top of the stack.
void push(int x)
{
if (isFull() == true)
resizeStack();
top = top + 1;
stack[top] = x;
}
class StackDynamicArray
{
private:
int *stack;
int top;
int capacity;
void resizeStack()
{
int *temp = new int[2 * capacity];
for (int i = 0; i < capacity; i = i + 1)
temp[i] = stack[i];
delete [] stack;
stack = temp;
capacity = 2 * capacity;
}
public:
StackDynamicArray(int size)
{
stack = new int[size];
capacity = size;
top = -1;
}
void push(int x)
{
if (isFull())
resizeStack();
top = top + 1;
stack[top] = x;
}
int pop()
{
if (isEmpty())
{
cout << "Stack Underflow!" << endl;
return INT_MIN;
}
int temp = stack[top];
top = top - 1;
return temp;
}
int peek()
{
if (isEmpty())
{
cout << "Stack is empty!" << endl;
return INT_MIN;
}
return stack[top];
}
int size()
{
return top + 1;
}
bool isEmpty()
{
return top == -1;
}
bool isFull()
{
return top == capacity - 1;
}
};
Note: In programming, dynamic-sized arrays are data structures that can hold a varying number of items. For example, in C++ we have vectors, in Python we have lists, and in Java we have ArrayLists. These data structures have the ability to automatically adjust their size as items are added or removed.
A singly-linked list is a linear data structure where each element, called a node, stores a value and a pointer to the next element in the list. We can use a singly-linked list to implement a stack data structure by representing the head of the linked list as the top of the stack. This allows us to easily add and remove elements from the head of the list to perform push and pop operations on the stack.
We define a LinkedListStack class, which has the following private member attributes.
We define following public member methods in LinkedListStack class:
void push(int data): We adds a new element to the top of the stack. For this, we creates a new Node object with the given data, sets the next pointer of the new node to the current top element of the stack, and then updates the stackTop pointer to the new node. We also increments the stackSize counter.
void push(int data)
{
Node* new_node = new Node(data);
new_node->next = stackTop;
stackTop = new_node;
stackSize = stackSize + 1;
}
int pop(): We removes the top element from the stack. For this, we checks if the stack is empty, and if it is, we prints an error message. If the stack is not empty, we stores a pointer to the top element in a temporary variable, updates the stackTop pointer to the next element in the list, stores the value of the top element in a temp variable, deletes the top element, and decrements the stackSize counter. Finally, we returns the value of the top element stored in temp variable.
int pop()
{
if (stackSize == 0)
{
cout << "Stack is empty" << endl;
return INT_MIN;
}
Node* temp = stackTop;
stackTop = stackTop->next;
int data = temp->data;
delete temp;
stackSize = stackSize - 1;
return data;
}
int getSize(): We returns the value stored in stackSize variable.
bool isEmpty(): If stackSize is equal to 0, we returns true otherwise we return false otherwise.
int peek(): We checks if the stack is empty, and if it is, we prints an error message. If the stack is not empty, we returns the value stored in stackTop.
int getSize()
{
return stackSize;
}
bool isEmpty()
{
return stackSize == 0;
}
int peek()
{
if (stackSize == 0)
{
cout << "Stack is empty" << endl;
return INT_MIN;
}
return stackTop->data;
}
// Node class for a singly-linked list
class Node
{
public:
int data;
Node* next;
Node(int data) : data(data), next(NULL) {}
};
// Stack class
class LinkedListStack
{
private:
Node* stackTop;
int stackSize;
public:
Stack() : stackTop(NULL), size(0) {}
// Push an element to the top of the stack
void push(int data)
{
Node* new_node = new Node(data);
new_node->next = stackTop;
stackTop = new_node;
stackSize = stackSize + 1;
}
// Pop the top element from the stack
int pop()
{
if (stackSize == 0)
{
cout << "Error: stack is empty" << endl;
return INT_MIN;
}
Node* temp = stackTop;
stackTop = stackTop->next;
int data = temp->data;
delete temp;
stackSize = stackSize - 1;
return data;
}
// Return the size of the stack
int getSize()
{
return stackSize;
}
// Check if the stack is empty
bool isEmpty()
{
return stackSize == 0;
}
// Return the top element of the stack
int peek()
{
if (stackSize == 0)
{
cout << "Error: stack is empty" << endl;
return INT_MIN;
}
return stackTop->data;
}
};
One advantage of using a linked list to implement a stack is that it can dynamically adjust its size at runtime to meet changing needs. However, this approach does have a drawback: it requires the use of pointers, which can consume additional memory.
Explore this blog to learn more: Application of stack data structure
Enjoy learning, enjoy algorithms!