Stack
A Last-In-First-Out (LIFO) data structure where elements are added and removed from the same end (the top).
Overview
Stacks follow the principle of “last in, first out.” Think of a stack of plates - you can only add or remove plates from the top. This implementation uses a singly linked list internally for O(1) push and pop operations.
Implementation
Class Definition
import { SinglyLinkedList } from '../lists/singly-linked-list/singly-linked-list' ;
class Stack < T = number > {
#stack = new SinglyLinkedList < T >();
// ... methods
}
Reference : stack.ts:1-4
The implementation uses a private singly linked list (#stack) for storage, leveraging its O(1) head insertion and deletion.
API Reference
push(item: T): void
Adds an item to the top of the stack.
const stack = new Stack < number >();
stack . push ( 1 );
stack . push ( 2 );
stack . push ( 3 );
// Stack: [1][2][3] (3 is on top)
Complexity : O(1)
Reference : stack.ts:10-12
pop(): T | undefined
Removes and returns the top element from the stack.
const stack = new Stack < number >();
stack . push ( 1 );
stack . push ( 2 );
const top = stack . pop (); // returns 2
// Stack: [1]
Returns : The removed element, or undefined if stack is empty
Complexity : O(1)
Reference : stack.ts:18-20
peek(): T | undefined
Returns the top element without removing it.
const stack = new Stack < number >();
stack . push ( 1 );
stack . push ( 2 );
const top = stack . peek (); // returns 2
// Stack: [1][2] (unchanged)
Returns : The top element, or undefined if stack is empty
Complexity : O(1)
Reference : stack.ts:25-27
isEmpty(): boolean
Checks if the stack is empty.
const stack = new Stack < number >();
console . log ( stack . isEmpty ()); // true
stack . push ( 1 );
console . log ( stack . isEmpty ()); // false
Complexity : O(1)
Reference : stack.ts:32-34
size: number
Gets the number of elements in the stack.
const stack = new Stack < number >();
console . log ( stack . size ); // 0
stack . push ( 1 );
stack . push ( 2 );
console . log ( stack . size ); // 2
Complexity : O(1)
Reference : stack.ts:36-38
toString(): string
Returns a string representation of the stack.
const stack = new Stack < number >();
stack . push ( 1 );
stack . push ( 2 );
stack . push ( 3 );
console . log ( stack . toString ()); // "[1][2][3]"
Complexity : O(n)
Reference : stack.ts:40-53
Problems & Solutions
Problem 1: Sort Stack
Sort a stack such that the smallest items are on top, using only one additional stack.
/**
* Time complexity: O(n²)
* Space complexity: O(n)
*/
function sortStack ( stack : Stack < number >) {
const auxStack = new Stack < number >();
while ( ! stack . isEmpty ()) {
const temp = stack . pop ();
// Move elements from aux back to main if they're greater
while ( ! auxStack . isEmpty () && temp ! > auxStack . peek () ! ) {
stack . push ( auxStack . pop () ! );
}
auxStack . push ( temp ! );
}
// Transfer back to original stack
while ( ! auxStack . isEmpty ()) {
stack . push ( auxStack . pop () ! );
}
}
// Example: [4][2][1][3] -> [1][2][3][4]
Reference : stack.problems.test.ts:5-37
Problem 2: Base Converter
Convert a decimal number to any base (2-36) using a stack.
/**
* Time complexity: O(log n)
* Space complexity: O(log n)
*/
function baseConverter ( num : number , base : number ) : string {
const stack = new Stack < number >();
const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ;
while ( num > 0 ) {
const remainder = Math . floor ( num % base );
stack . push ( remainder );
num = Math . floor ( num / base );
}
let output = '' ;
while ( ! stack . isEmpty ()) {
output += digits [ stack . pop () ! ];
}
return output ;
}
// Examples:
// baseConverter(10, 2) -> "1010"
// baseConverter(100345, 16) -> "187F9"
Reference : stack.problems.test.ts:40-68
Complexity Summary
Operation Time Complexity Space Complexity push O(1) O(1) pop O(1) O(1) peek O(1) O(1) isEmpty O(1) O(1) size O(1) O(1) toString O(n) O(1)
Common Use Cases
Function Call Stack
// The JavaScript engine uses a stack for function calls
function recursive ( n : number ) {
if ( n === 0 ) return ;
recursive ( n - 1 ); // Each call is pushed onto the call stack
}
Expression Evaluation
// Evaluate postfix expressions
// Example: "3 4 + 2 *" = (3 + 4) * 2 = 14
function evaluatePostfix ( expression : string ) : number {
const stack = new Stack < number >();
const tokens = expression . split ( ' ' );
for ( const token of tokens ) {
if ( ! isNaN ( Number ( token ))) {
stack . push ( Number ( token ));
} else {
const b = stack . pop () ! ;
const a = stack . pop () ! ;
// Apply operator and push result
}
}
return stack . pop () ! ;
}
Parentheses Matching
function isBalanced ( str : string ) : boolean {
const stack = new Stack < string >();
const pairs : Record < string , string > = {
')' : '(' ,
']' : '[' ,
'}' : '{'
};
for ( const char of str ) {
if ([ '(' , '[' , '{' ]. includes ( char )) {
stack . push ( char );
} else if ( pairs [ char ]) {
if ( stack . pop () !== pairs [ char ]) return false ;
}
}
return stack . isEmpty ();
}
Undo/Redo Functionality
class TextEditor {
private history = new Stack < string >();
private redoStack = new Stack < string >();
private currentText = '' ;
type ( text : string ) {
this . history . push ( this . currentText );
this . currentText += text ;
this . redoStack = new Stack (); // Clear redo on new action
}
undo () {
if ( ! this . history . isEmpty ()) {
this . redoStack . push ( this . currentText );
this . currentText = this . history . pop () ! ;
}
}
redo () {
if ( ! this . redoStack . isEmpty ()) {
this . history . push ( this . currentText );
this . currentText = this . redoStack . pop () ! ;
}
}
}
Key Characteristics
Advantages:
Simple and intuitive LIFO principle
O(1) push and pop operations
Memory efficient
Natural fit for recursive algorithms
Limitations:
No random access to elements
Can only access top element
Stack overflow with too many elements
Not suitable for priority-based access
When to Use a Stack
Depth-first search, maze solving, N-Queens problem - stacks naturally support backtracking by maintaining state history.
Infix to postfix conversion, calculator implementations, compiler syntax analysis - stacks help maintain operator precedence.
Reverse strings, arrays, or any sequence - push all elements and pop them back out in reverse order.
Matching brackets, HTML tag validation, tree traversal - stacks track nesting levels.
For scenarios requiring access to both ends, consider using a Queue or Deque instead.
Queue FIFO alternative to LIFO stack
Singly Linked List Underlying implementation