Learnitweb

Understanding Stack Abstract Data Type (ADT)

Introduction to Stack ADT

In computer science, a Stack is an abstract data type (ADT) that serves as a collection of elements, with two principal operations:

  1. Push: Adds an item to the top of the stack.
  2. Pop: Removes the item from the top of the stack.

A Stack follows the LIFO (Last In, First Out) principle. This means the last item inserted will be the first item removed. It is similar to a deck of cards where you can only access the top card.

Why is Stack ADT Important?

The Stack ADT is a fundamental concept in computer science because it forms the basis for many system-level functionalities, including:

  1. Function call management (Call Stack)
  2. Undo operations in text editors
  3. Browser history management
  4. Recursion handling

The Stack ADT can be implemented using:

  • Array-based stack
  • Linked list-based stack

Stack Operations

Push Operation

The Push operation is used to insert a new element on the top of the stack.

Example:

  • Push(10)
  • Push(20)
  • Push(30)

Stack now looks like:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
30 <- Top
20
10
30 <- Top 20 10
30 <- Top
20
10

Pop Operation

The Pop operation removes the topmost element from the stack.

Example:

  • Pop() -> Removes 30
  • Pop() -> Removes 20

Stack now looks like:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
10 <- Top
10 <- Top
10 <- Top

Peek Operation

The Peek operation returns the topmost element without removing it from the stack.

Example:

  • Peek() -> Returns 10
  • Stack remains unchanged

Stack Memory vs Heap Memory

What is Stack Memory?

Stack Memory is a region in Random Access Memory (RAM) that stores local variables, method calls, and function call references.

  • Fast access: Stack memory is fast because memory is allocated and deallocated automatically.
  • Small size: It has a fixed small size, generally limited.
  • Stores function calls and local variables.

What is Heap Memory?

Heap Memory is also a region in RAM where dynamically allocated objects are stored.

  • Large size: Heap memory is larger than stack memory.
  • Slow access: Accessing data from heap is slower than stack.
  • Holds objects, arrays, and reference data.

Stack Frame in Stack Memory

What is a Stack Frame?

A Stack Frame is a block of memory in the stack that stores:

  • Method parameters
  • Local variables
  • Return address

Every time a function/method is called, a stack frame is created. When the function ends, the stack frame is automatically destroyed.

Function Call Flow in Stack Memory

Let’s understand how stack memory works with a function call.

Example Code

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class StackExample {
public static void main(String[] args) {
function1();
}
static void function1() {
int a = 10;
function2();
}
static void function2() {
int b = 20;
function3();
}
static void function3() {
int c = 30;
}
}
public class StackExample { public static void main(String[] args) { function1(); } static void function1() { int a = 10; function2(); } static void function2() { int b = 20; function3(); } static void function3() { int c = 30; } }
public class StackExample {
   public static void main(String[] args) {
      function1();
   }

   static void function1() {
      int a = 10;
      function2();
   }

   static void function2() {
      int b = 20;
      function3();
   }

   static void function3() {
      int c = 30;
   }
}

Stack Memory Flow

  1. function1() is called → Stack Frame for function1 is created.
  2. function2() is called → Stack Frame for function2 is created.
  3. function3() is called → Stack Frame for function3 is created.
  4. function3() finishes → Stack Frame is removed.
  5. function2() finishes → Stack Frame is removed.
  6. function1() finishes → Stack Frame is removed.

Objects in Heap Memory

When we create an object using new keyword, it is stored in the Heap Memory.

Example Code

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class House {
int windows;
int doors;
public House(int windows, int doors) {
this.windows = windows;
this.doors = doors;
}
public static void main(String[] args) {
House myHouse = new House(5, 10);
}
}
public class House { int windows; int doors; public House(int windows, int doors) { this.windows = windows; this.doors = doors; } public static void main(String[] args) { House myHouse = new House(5, 10); } }
public class House {
   int windows;
   int doors;

   public House(int windows, int doors) {
      this.windows = windows;
      this.doors = doors;
   }

   public static void main(String[] args) {
      House myHouse = new House(5, 10);
   }
}

Memory Allocation

  • Heap Memory: The House object is stored in heap.
  • Stack Memory: The reference myHouse is stored in stack.

Garbage Collection

What is Garbage Collection?

Garbage Collection (GC) is an automatic process of freeing up memory from unused objects in the heap.

Example

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
House house1 = new House(5, 10);
House house2 = new House(6, 12);
house1 = null; // Eligible for Garbage Collection
House house1 = new House(5, 10); House house2 = new House(6, 12); house1 = null; // Eligible for Garbage Collection
House house1 = new House(5, 10);
House house2 = new House(6, 12);
house1 = null;  // Eligible for Garbage Collection

When house1 reference is set to null, the object is no longer accessible, making it eligible for garbage collection.

Stack Implementation with Linked List

Node.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> nextNode;
public Node(T data){
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Node<T> getNextNode() {
return nextNode;
}
public void setNextNode(Node<T> nextNode) {
this.nextNode = nextNode;
}
@Override
public String toString() {
return this.data.toString();
}
}
public class Node<T extends Comparable<T>> { private T data; private Node<T> nextNode; public Node(T data){ this.data = data; } public T getData() { return data; } public void setData(T data) { this.data = data; } public Node<T> getNextNode() { return nextNode; } public void setNextNode(Node<T> nextNode) { this.nextNode = nextNode; } @Override public String toString() { return this.data.toString(); } }
public class Node<T extends Comparable<T>> {

	private T data;
	private Node<T> nextNode;
	
	public Node(T data){
		this.data = data;
	}
	
	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

	public Node<T> getNextNode() {
		return nextNode;
	}

	public void setNextNode(Node<T> nextNode) {
		this.nextNode = nextNode;
	}

	@Override
	public String toString() {
		return this.data.toString();
	}
}

Stack.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Stack<T extends Comparable<T>> {
private Node<T> root;
private int count;
// O(1) constant time
public void push(T newData){
this.count++;
if( this.root == null ){
this.root = new Node<>(newData);
}else{
Node<T> oldRoot = this.root;
this.root = new Node<>(newData);
this.root.setNextNode(oldRoot);
}
}
public int size(){
return this.count;
}
public T pop(){
T itemToPop = this.root.getData();
this.root = this.root.getNextNode();
this.count--;
return itemToPop;
}
public boolean isEmpty(){
return this.root == null;
}
}
public class Stack<T extends Comparable<T>> { private Node<T> root; private int count; // O(1) constant time public void push(T newData){ this.count++; if( this.root == null ){ this.root = new Node<>(newData); }else{ Node<T> oldRoot = this.root; this.root = new Node<>(newData); this.root.setNextNode(oldRoot); } } public int size(){ return this.count; } public T pop(){ T itemToPop = this.root.getData(); this.root = this.root.getNextNode(); this.count--; return itemToPop; } public boolean isEmpty(){ return this.root == null; } }
public class Stack<T extends Comparable<T>> {

	private Node<T> root;
	private int count;
	
	// O(1) constant time
	public void push(T newData){
		
		this.count++;
		
		if( this.root == null ){
			this.root = new Node<>(newData);
		}else{
			Node<T> oldRoot = this.root;
			this.root = new Node<>(newData);
			this.root.setNextNode(oldRoot);
		}
	}
	
	
	public int size(){
		return this.count;
	}
	
	
	public T pop(){
		T itemToPop = this.root.getData();
		this.root = this.root.getNextNode();
		this.count--;
		return itemToPop;
	}

	
	public boolean isEmpty(){
		return this.root == null;
	}
}

App.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class App {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
System.out.println(stack.pop());
System.out.println(stack.size());
System.out.println(stack.pop());
System.out.println(stack.isEmpty());
}
}
public class App { public static void main(String[] args) { Stack<Integer> stack = new Stack<>(); stack.push(10); stack.push(20); System.out.println(stack.pop()); System.out.println(stack.size()); System.out.println(stack.pop()); System.out.println(stack.isEmpty()); } }
public class App {

	public static void main(String[] args) {
		
		Stack<Integer> stack = new Stack<>();
		
		stack.push(10);
		stack.push(20);
		
		System.out.println(stack.pop());
		
		System.out.println(stack.size());
		
		System.out.println(stack.pop());
		
		System.out.println(stack.isEmpty());

	}

}

Stack implementation with Array

Stack.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Stack<Item> {
private Item[] stack;
private int numberOfItems;
public Stack(){
this.stack = ( Item[] ) new Object[1];
}
public void push(Item item){
if( numberOfItems == this.stack.length ){
resize(2*this.stack.length);
}
this.stack[numberOfItems++] = item;
}
public Item pop(){
Item itemToPop = this.stack[--numberOfItems];
if( numberOfItems > 0 && numberOfItems == this.stack.length/4 ){
resize(this.stack.length/2);
}
return itemToPop;
}
public boolean isEmpty(){
return this.numberOfItems == 0;
}
public int size(){
return this.numberOfItems;
}
// O(n)
private void resize(int capacity) {
Item[] stackCopy = ( Item[] ) new Object[capacity];
for(int i=0;i<numberOfItems;i++){
stackCopy[i]=this.stack[i];
}
this.stack = stackCopy;
}
}
public class Stack<Item> { private Item[] stack; private int numberOfItems; public Stack(){ this.stack = ( Item[] ) new Object[1]; } public void push(Item item){ if( numberOfItems == this.stack.length ){ resize(2*this.stack.length); } this.stack[numberOfItems++] = item; } public Item pop(){ Item itemToPop = this.stack[--numberOfItems]; if( numberOfItems > 0 && numberOfItems == this.stack.length/4 ){ resize(this.stack.length/2); } return itemToPop; } public boolean isEmpty(){ return this.numberOfItems == 0; } public int size(){ return this.numberOfItems; } // O(n) private void resize(int capacity) { Item[] stackCopy = ( Item[] ) new Object[capacity]; for(int i=0;i<numberOfItems;i++){ stackCopy[i]=this.stack[i]; } this.stack = stackCopy; } }
public class Stack<Item> {

	private Item[] stack;
	private int numberOfItems;
	
	public Stack(){
		this.stack = ( Item[] ) new Object[1];
	}

	public void push(Item item){
		
		if( numberOfItems == this.stack.length ){
			resize(2*this.stack.length);
		}
		
		this.stack[numberOfItems++] = item;
	}
	
	public Item pop(){
		Item itemToPop = this.stack[--numberOfItems];
		
		if( numberOfItems > 0 && numberOfItems == this.stack.length/4 ){
			resize(this.stack.length/2);
		}
		
		return itemToPop;
	}
	
	public boolean isEmpty(){
		return this.numberOfItems == 0;
	}
	
	public int size(){
		return this.numberOfItems;
	}

	// O(n)
	private void resize(int capacity) {
		
		Item[] stackCopy = ( Item[] ) new Object[capacity];
		
		for(int i=0;i<numberOfItems;i++){
			stackCopy[i]=this.stack[i];
		}
		
		this.stack = stackCopy;
	}
}

App.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class App {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
stack.push("Roger");
stack.push("James");
System.out.println(stack.size());
System.out.println(stack.pop());
System.out.println(stack.size());
}
}
public class App { public static void main(String[] args) { Stack<String> stack = new Stack<>(); stack.push("Roger"); stack.push("James"); System.out.println(stack.size()); System.out.println(stack.pop()); System.out.println(stack.size()); } }
public class App {

	public static void main(String[] args) {
		
		Stack<String> stack = new Stack<>();
		
		stack.push("Roger");
		stack.push("James");
		
		System.out.println(stack.size());
		
		System.out.println(stack.pop());

		System.out.println(stack.size());
		
	}

}

Stack in Java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Stack<E>
extends Vector<E>
public class Stack<E> extends Vector<E>
public class Stack<E>
extends Vector<E>

The Stack class represents a last-in-first-out (LIFO) stack of objects. It extends class Vector with five operations that allow a vector to be treated as a stack. The usual push and pop operations are provided, as well as a method to peek at the top item on the stack, a method to test for whether the stack is empty, and a method to search the stack for an item and discover how far it is from the top.

When a stack is first created, it contains no items.

A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. For example:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Deque<Integer> stack = new ArrayDeque<Integer>();
Deque<Integer> stack = new ArrayDeque<Integer>();
Deque<Integer> stack = new ArrayDeque<Integer>();