Stacks and Queues

  • Stacks are a “first in, first out” type of system

  • Stacks can “push” new items onto the top, and “pop” elements from the top of the stack and return them

  • Nodes can be a good way of representing a stack

  • Queues are a data structure where you can enqueue (add a new item to the “back of the line” so to speak) and Dequeue, or remove an item from the front of the queue and return it.

  • Nodes and link lists are usually better and more versatile than arrays
    • Along with the memory cost of storing additional pointers, the pointer assignments add a small, constant cost to both the push and pop operations. We’re no longer setting a single array value and incrementing an index. However, as with all dynamic data structures, the tradeoff is increased flexibility: a linked list can grow and shrink with the data. We no longer have to worry about filling up our array or paying the additional costs to increase the array’s size.

  • Different data structures both allow the programmer to use data in different ways and strongly impact the behavior of the algorithms working with that data. Both stacks and queues store objects, both can be implemented with either arrays or linked lists, and both can handle insertions and removals efficiently. From the perspective of simply storing bits, either will suffice. However, the way they handle the data, and specifically the order in which they return their items, gives these similar data structures radically different behavior. Stacks return the newest data they’ve stored, making them ideal for prioritizing the most recent items. Queues, in contrast, always return the oldest data they’ve stored, making them ideal for cases where we need to handle items in the order in which they arrive.

  • Oh, here’s an example of a stack I cloned

#include <stdio.h>
#include <iostream>
using namespace std;

// Node Structure for the Linked List

struct Node {
    int data;       // Value stored in the node
    Node* next;     // Pointer to the next node in the list

    // Constructor to initialize a node with data
    Node(int value) {
        data = value;
        next = nullptr;
    }
};


// Stack Class Using Singly Linked List


class Stack {
private:
    Node* top; // Pointer to the top element of the stack

public:

    // Constructor
    // Initializes the stack as empty by setting top to nullptr

    Stack() {
        top = nullptr;
    }


    // Destructor
    // Frees all memory by popping all elements

    ~Stack() {
        while (!isEmpty()) {
            pop(); // Automatically deletes all nodes
        }
    }


    // push(int value)
    // Adds a new element to the top of the stack

    void push(int value) {
        Node* newNode = new Node(value);  // Create a new node with the given value
        newNode->next = top;              // New node points to the current top
        top = newNode;                    // Update top to point to the new node
        cout << "Pushed: " << value << endl;
    }


    // pop()
    // Removes the top element from the stack

    void pop() {
        if (isEmpty()) {
            cout << "Stack Underflow! Nothing to pop.\n";
            return;
        }

        Node* temp = top;       // Temporary pointer to the top node
        top = top->next;        // Move top to the next node in the list
        std::cout << "Popped: " << temp->data << endl;
        delete temp;            // Free memory of the removed node
    }


    // peek()
    // Returns the top element without removing it

    int peek() {
        if (isEmpty()) {
            cout << "Stack is empty. Nothing to peek.\n";
            return -1; // Return sentinel value (could also throw exception)
        }
        return top->data; // Return value at the top
    }


    // isEmpty()
    // Checks whether the stack is empty

    bool isEmpty() {
        return top == nullptr; // True if top is null, false otherwise
    }


    // display()
    // Prints all elements in the stack from top to bottom

    void display() {
        if (isEmpty()) {
            cout << "Stack is empty.\n";
            return;
        }

        Node* temp = top; // Start from the top
        cout << "Stack elements (top to bottom): ";
        while (temp != nullptr) {
            cout << temp->data << " "; // Print current node's data
            temp = temp->next;         // Move to the next node
        }
        cout << endl;
    }
};


// Main Function: Demonstrates Stack Operations


int main() {
    Stack s; // Create a Stack instance

    // Push elements onto the stack
    s.push(10);
    s.push(20);
    s.push(30);

    // Display current elements
    s.display(); // Should show: 30 20 10

    // Peek at the top element
    cout << "Top element is: " << s.peek() << endl;

    // Pop one element
    s.pop();     // Should remove 30
    s.display(); // Should show: 20 10

    // Check if the stack is empty
    cout << "Is stack empty? " << (s.isEmpty() ? "Yes" : "No") << endl;

    return 0;
}