WikiGalaxy

Personalize

Stacks in Java

Introduction to Stacks:

A stack is a linear data structure that follows the Last In First Out (LIFO) principle. The element that is added last is the one to be removed first.

Basic Operations:

The basic operations of a stack include push (adding an element to the top), pop (removing the top element), and peek (viewing the top element without removing it).

    Step 1: Initialize an empty stack.
    Step 2: Push elements onto the stack.
    Step 3: Use pop to remove elements.
    Step 4: Use peek to view the top element.
  

import java.util.Stack;

public class StackExample {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        
        // Push elements
        stack.push(10);
        stack.push(20);
        stack.push(30);
        
        // Peek the top element
        System.out.println("Top element: " + stack.peek());
        
        // Pop elements
        System.out.println("Popped element: " + stack.pop());
        System.out.println("Top element after pop: " + stack.peek());
    }
}
    

Use Case: Backtracking Algorithms

Stacks are used in backtracking algorithms to store the state of the current solution and backtrack when necessary.

Console Output:

Top element: 30

Popped element: 30

Top element after pop: 20

Stack Implementation Using Linked List

Why Use Linked List for Stacks?

Using a linked list to implement a stack can be more efficient in terms of memory allocation, as it does not require resizing like an array-based stack.

    Step 1: Create a Node class with data and next pointer.
    Step 2: Implement push by adding a new node at the head.
    Step 3: Implement pop by removing the head node.
    Step 4: Implement peek by returning the data of the head node.
  

class Node {
    int data;
    Node next;
    
    Node(int data) {
        this.data = data;
        this.next = null;
    }
}

class LinkedListStack {
    private Node head;

    public void push(int data) {
        Node newNode = new Node(data);
        newNode.next = head;
        head = newNode;
    }

    public int pop() {
        if (head == null) throw new IllegalStateException("Stack is empty");
        int data = head.data;
        head = head.next;
        return data;
    }

    public int peek() {
        if (head == null) throw new IllegalStateException("Stack is empty");
        return head.data;
    }
}

public class Main {
    public static void main(String[] args) {
        LinkedListStack stack = new LinkedListStack();
        stack.push(10);
        stack.push(20);
        System.out.println("Top element: " + stack.peek());
        System.out.println("Popped element: " + stack.pop());
    }
}
    

Performance Consideration

Linked list-based stacks can handle dynamic size changes efficiently but may have a slightly higher overhead due to node creation.

Console Output:

Top element: 20

Popped element: 20

Stack Applications: Expression Evaluation

Infix to Postfix Conversion

Stacks are used to convert infix expressions (where operators are between operands) to postfix expressions (where operators follow their operands).

    Step 1: Initialize an empty stack and result string.
    Step 2: Traverse the expression.
    Step 3: If operand, add to result.
    Step 4: If operator, pop from stack until stack top has less precedence.
    Step 5: Push current operator on stack.
    Step 6: Pop remaining operators from stack to result.
  

import java.util.Stack;

public class InfixToPostfix {
    public static int precedence(char ch) {
        switch (ch) {
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            case '^':
                return 3;
        }
        return -1;
    }

    public static String infixToPostfix(String expression) {
        StringBuilder result = new StringBuilder();
        Stack<Character> stack = new Stack<>();

        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);

            if (Character.isLetterOrDigit(c)) {
                result.append(c);
            } else if (c == '(') {
                stack.push(c);
            } else if (c == ')') {
                while (!stack.isEmpty() && stack.peek() != '(') {
                    result.append(stack.pop());
                }
                stack.pop();
            } else {
                while (!stack.isEmpty() && precedence(c) <= precedence(stack.peek())) {
                    result.append(stack.pop());
                }
                stack.push(c);
            }
        }

        while (!stack.isEmpty()) {
            result.append(stack.pop());
        }

        return result.toString();
    }

    public static void main(String[] args) {
        String expression = "a+b*(c^d-e)^(f+g*h)-i";
        System.out.println("Postfix expression: " + infixToPostfix(expression));
    }
}
    

Efficiency in Parsing

Using stacks for expression conversion ensures that operators are applied in the correct order, respecting precedence and associativity rules.

Console Output:

Postfix expression: abcd^e-fgh*+^*+i-

Stack Applications: Undo Mechanism

Implementing Undo Functionality

Stacks can be used to implement undo functionality in applications by storing previous states and reverting to them when needed.

    Step 1: Initialize a stack to store states.
    Step 2: On action, push current state to stack.
    Step 3: On undo, pop from stack and revert to previous state.
  

import java.util.Stack;

class TextEditor {
    private Stack<String> history = new Stack<>();
    private String currentText = "";

    public void type(String text) {
        history.push(currentText);
        currentText += text;
    }

    public void undo() {
        if (!history.isEmpty()) {
            currentText = history.pop();
        }
    }

    public String getText() {
        return currentText;
    }

    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        editor.type("Hello");
        editor.type(" World");
        System.out.println("Current text: " + editor.getText());
        editor.undo();
        System.out.println("After undo: " + editor.getText());
    }
}
    

User Experience Enhancement

Providing undo functionality enhances user experience by allowing users to revert unintended changes easily.

Console Output:

Current text: Hello World

After undo: Hello

Stack Applications: Balanced Parentheses

Checking Balanced Parentheses

Stacks can be used to check for balanced parentheses in expressions, ensuring each opening bracket has a corresponding closing bracket.

    Step 1: Initialize an empty stack.
    Step 2: Traverse the expression.
    Step 3: Push opening brackets onto the stack.
    Step 4: On closing bracket, pop from stack and check for match.
    Step 5: If stack is empty at end, parentheses are balanced.
  

import java.util.Stack;

public class BalancedParentheses {
    public static boolean isBalanced(String expression) {
        Stack<Character> stack = new Stack<>();

        for (char ch : expression.toCharArray()) {
            if (ch == '(' || ch == '{' || ch == '[') {
                stack.push(ch);
            } else if (ch == ')' || ch == '}' || ch == ']') {
                if (stack.isEmpty()) return false;
                char open = stack.pop();
                if (!isMatchingPair(open, ch)) return false;
            }
        }

        return stack.isEmpty();
    }

    private static boolean isMatchingPair(char open, char close) {
        return (open == '(' && close == ')') ||
               (open == '{' && close == '}') ||
               (open == '[' && close == ']');
    }

    public static void main(String[] args) {
        String expression = "{[()]}";
        System.out.println("Is balanced: " + isBalanced(expression));
    }
}
    

Ensuring Syntax Validity

Checking for balanced parentheses is crucial in compilers and interpreters to ensure the syntax validity of the code.

Console Output:

Is balanced: true

Stack Applications: Browser History

Implementing Browser Back Functionality

Stacks are ideal for implementing browser back functionality, allowing users to navigate to previously visited pages.

    Step 1: Use a stack to store URLs of visited pages.
    Step 2: On visiting a new page, push the current URL onto the stack.
    Step 3: On back action, pop from stack to get the previous URL.
  

import java.util.Stack;

public class BrowserHistory {
    private Stack<String> history = new Stack<>();
    private String currentPage = "home";

    public void visit(String url) {
        history.push(currentPage);
        currentPage = url;
    }

    public void back() {
        if (!history.isEmpty()) {
            currentPage = history.pop();
        }
    }

    public String getCurrentPage() {
        return currentPage;
    }

    public static void main(String[] args) {
        BrowserHistory browser = new BrowserHistory();
        browser.visit("page1");
        browser.visit("page2");
        System.out.println("Current page: " + browser.getCurrentPage());
        browser.back();
        System.out.println("After back: " + browser.getCurrentPage());
    }
}
    

Enhancing User Navigation

Using stacks for browser history provides an intuitive way for users to navigate back through their browsing session.

Console Output:

Current page: page2

After back: page1

Stack Applications: Function Call Stack

Understanding Function Call Stacks

In programming, the call stack is used to store information about the active subroutines of a computer program, facilitating function calls and returns.

    Step 1: On function call, push function context onto stack.
    Step 2: Execute function body.
    Step 3: On return, pop function context from stack.
  

public class CallStackExample {
    public static void main(String[] args) {
        System.out.println("Main start");
        functionA();
        System.out.println("Main end");
    }

    public static void functionA() {
        System.out.println("Function A start");
        functionB();
        System.out.println("Function A end");
    }

    public static void functionB() {
        System.out.println("Function B start");
        System.out.println("Function B end");
    }
}
    

Managing Execution Flow

The call stack is crucial for managing the execution flow of programs, particularly in recursive function calls.

Console Output:

Main start

Function A start

Function B start

Function B end

Function A end

Main end

Stack Applications: Depth-First Search (DFS)

Using Stacks in DFS

Depth-First Search (DFS) is a graph traversal algorithm that uses a stack to explore the vertices and edges of a graph.

    Step 1: Initialize an empty stack and push the starting vertex.
    Step 2: Pop a vertex from the stack and visit it.
    Step 3: Push all adjacent unvisited vertices onto the stack.
    Step 4: Repeat until the stack is empty.
  

import java.util.*;

public class DFSExample {
    private LinkedList<Integer>[] adjList;

    public DFSExample(int vertices) {
        adjList = new LinkedList[vertices];
        for (int i = 0; i < vertices; i++) {
            adjList[i] = new LinkedList<>();
        }
    }

    public void addEdge(int src, int dest) {
        adjList[src].add(dest);
    }

    public void dfs(int startVertex) {
        boolean[] visited = new boolean[adjList.length];
        Stack<Integer> stack = new Stack<>();
        stack.push(startVertex);

        while (!stack.isEmpty()) {
            int vertex = stack.pop();
            if (!visited[vertex]) {
                visited[vertex] = true;
                System.out.print(vertex + " ");
                for (int adj : adjList[vertex]) {
                    if (!visited[adj]) {
                        stack.push(adj);
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        DFSExample graph = new DFSExample(4);
        graph.addEdge(0, 1);
        graph.addEdge(0, 2);
        graph.addEdge(1, 2);
        graph.addEdge(2, 0);
        graph.addEdge(2, 3);
        graph.addEdge(3, 3);

        System.out.print("DFS starting from vertex 2: ");
        graph.dfs(2);
    }
}
    

Exploring Graphs Efficiently

DFS is used in various applications such as finding connected components, topological sorting, and solving puzzles.

Console Output:

DFS starting from vertex 2: 2 3 0 1

logo of wikigalaxy

Newsletter

Subscribe to our newsletter for weekly updates and promotions.

Privacy Policy

 • 

Terms of Service

Copyright © WikiGalaxy 2025