A stack is a linear data structure that follows the LIFO (Last In, First Out) principle. This means that the last element added to the stack is the first one to be removed. It’s like a stack of plates: you can only take the top plate off first before accessing the others underneath.
There are two common ways to implement a stack:
Both implementations provide similar functionality but have different underlying structures and performance characteristics.
A stack supports the following fundamental operations:
x to the top of the stack.In this implementation, an array is used to store the stack’s elements, and a variable (often called top) keeps track of the index of the last element added to the stack.
#include <iostream>
using namespace std;
class Stack {
private:
int top;
int maxSize;
int *arr;
public:
// Constructor to initialize the stack with a fixed size
Stack(int size) {
maxSize = size;
arr = new int[maxSize];
top = -1; // Stack is initially empty
}
// Push operation to add an element to the stack
void push(int x) {
if (top == maxSize - 1) {
cout << "Stack Overflow\n";
return;
}
arr[++top] = x;
}
// Pop operation to remove the top element from the stack
int pop() {
if (top == -1) {
cout << "Stack Underflow\n";
return -1;
}
return arr[top--];
}
// Peek operation to view the top element
int peek() {
if (top == -1) {
cout << "Stack is empty\n";
return -1;
}
return arr[top];
}
// Check if the stack is empty
bool isEmpty() {
return top == -1;
}
// Destructor to clean up dynamically allocated memory
~Stack() {
delete[] arr;
}
};
int main() {
Stack stack(5);
stack.push(10);
stack.push(20);
stack.push(30);
cout << "Top element: " << stack.peek() << endl; // Output: 30
cout << "Popped element: " << stack.pop() << endl; // Output: 30
cout << "Popped element: " << stack.pop() << endl; // Output: 20
cout << "Is stack empty? " << (stack.isEmpty() ? "Yes" : "No") << endl; // Output: No
return 0;
}
push function increments the top index and places the new element there.pop function decrements the top index to remove the top element.maxSize), so we must handle overflow when the stack is full.In this implementation, a stack is built using a linked list, where each node contains a value and a pointer to the next node. The top of the stack is represented by the head of the linked list.
#include <iostream>
using namespace std;
// Node structure to represent an element in the linked list
struct Node {
int data; // Stores the data of the stack element
Node* next; // Pointer to the next node in the stack
};
// Stack class definition
class Stack {
private:
Node* top; // Points to the top of the stack
public:
// Constructor: Initializes the stack as empty
Stack() {
top = nullptr;
}
// Function to check if the stack is empty
bool isEmpty() {
return top == nullptr;
}
// Function to push an element onto the stack
void push(int value) {
Node* newNode = new Node(); // Create a new node
newNode->data = value; // Assign value to the node
newNode->next = top; // Link the new node to the current top
top = newNode; // Update the top to the new node
cout << value << " pushed to stack\n";
}
// Function to pop an element from the stack
int pop() {
if (isEmpty()) {
cout << "Stack Underflow: The stack is empty!\n";
return -1; // Return -1 if stack is empty
}
Node* temp = top; // Temporarily store the top node
int poppedValue = top->data; // Store the value to be popped
top = top->next; // Update the top pointer to the next node
delete temp; // Delete the popped node
cout << poppedValue << " popped from stack\n";
return poppedValue;
}
// Function to peek the top element without removing it
int peek() {
if (isEmpty()) {
cout << "Stack is empty\n";
return -1; // Return -1 if stack is empty
}
return top->data; // Return the data at the top
}
// Function to display the elements in the stack
void display() {
if (isEmpty()) {
cout << "Stack is empty\n";
return;
}
Node* temp = top;
cout << "Stack elements: ";
while (temp != nullptr) {
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
// Destructor: Clean up the memory
~Stack() {
while (!isEmpty()) {
pop(); // Pop all elements to delete them
}
}
};
int main() {
Stack stack;
stack.push(10); // Push elements
stack.push(20);
stack.push(30);
stack.push(40);
stack.display(); // Display stack elements
cout << "Top element is " << stack.peek() << endl; // Peek the top element
stack.pop(); // Pop an element
stack.pop(); // Pop another element
stack.display(); // Display stack after pops
return 0;
}
push function, a new node is created, and it becomes the new top of the stack.pop function, the current top node is removed, and the next node in the linked list becomes the new top.| Feature | Array Stack | Linked List Stack |
|---|---|---|
| Size | Fixed size (must define upfront). | Dynamic size (grows/shrinks). |
| Memory Usage | Allocates memory for entire array, even if partially used. | Allocates memory dynamically as needed. |
| Performance | Faster (array access is O(1)). | Slower (dynamic memory allocation is involved). |
| Implementation | Simple and easy to implement. | Slightly more complex to implement. |
| Risk | Stack overflow when full. | No overflow, but risk of running out of memory (if system memory is exhausted). |
Both the array and linked list implementations of a stack have their own use cases and benefits. Arrays are simpler and more efficient for fixed-size stacks, while linked lists provide flexibility in size, making them ideal for scenarios where the stack size is unknown or fluctuates frequently.
Open this section to load past papers