Foundational Concepts
Data structures define how information is arranged and stored within memory, while algorithms represent the precise sequences of instructions used to manipulate that information. Together, they form the backbone of efficient software development. Mastery involves understanding structural characteristics, identifying real-world application domains, implementing patterns in JavaScript, and rigorously evaluating performance metrics.
Solving computational problems through platforms like LeetCode sharpens pattern recognition. Focus on identifying reusable strategies, analyzing execution time, and optimizing memory consumption rather than memorizing isolated solutions.
Core Definitions
- Data Structures: Mechanisms for organizing raw data (e.g., stacks, queues, arrays, trees).
- Algorithms: Step-by-step procedures designed to solve specific computational tasks.
- Synergy: Structures provide the storage framework; algorithms execute the logic upon that framework.
Analyzing Computational Complexity
Performance evaluation relies on asymptotic notation, primarily Big-O, which estimates how resource consumption scales relative to input growth.
Time Complexity
This metric quantifies the maximum number of operations an algorithm performs as input size increases.
// Constant time: operation count remains fixed regardless of input size
function constantOperation(size) {
return size + 1;
}
// Linear time: iterations scale directly with input magnitude
function linearScan(items) {
for (let index = 0; index < items.length; index++) {
console.log(items[index]);
}
}
// Quadratic time: nested loops multiply operations proportionally
function quadraticComparison(matrixRows) {
const totalElements = matrixRows;
for (let r = 0; r < totalElements; r++) {
for (let c = 0; c < totalElements; c++) {
console.log(r, c);
}
}
}
// Logarithmic time: problem size halves with each iteration
function logarithmicSearch(targetLimit) {
let multiplier = 1;
while (multiplier < targetLimit) {
console.log(multiplier);
multiplier *= 2;
}
}
Space Complexity
This measures the temporary memory required during execution relative to input dimensions.
// O(1): Fixed memory footprint
function staticAllocation() {
let counter = 0;
return ++counter;
}
// O(n): Memory scales linearly with collection size
function dynamicArray(capacity) {
const buffer = [];
for (let i = 0; i < capacity; i++) {
buffer.push(i);
}
return buffer;
}
// O(n²): Matrix construction requires squared memory allocation
function allocateMatrix(dimensions) {
const grid = [];
for (let row = 0; row < dimensions; row++) {
grid[row] = [];
for (let col = 0; col < dimensions; col++) {
grid[row][col] = col;
}
}
return grid;
}
The Stack Abstraction
Stacks enforce Last-In, First-Out (LIFO) ordering. Elements are exclusively added to or removed from the top.
// Core operations
const dataStack = [];
dataStack.push("entryOne");
dataStack.push("entryTwo");
const recentItem = dataStack.pop(); // Returns "entryTwo"
Typical Use Cases: Syntax validation, functon call management, backtracking navigation, base conversion utilities.
Validating Nested Sequences
function validateBracketSequence(expression) {
if (expression.length % 2 !== 0) return false;
const openingChars = "([{";
const closingMap = { ")": "(", "]": "[", "}": "{" };
const trackingStack = [];
for (let char of expression) {
if (openingChars.includes(char)) {
trackingStack.push(char);
} else if (trackingStack.length === 0 || trackingStack.pop() !== closingMap[char]) {
return false;
}
}
return trackingStack.length === 0;
}
Tree Preorder Navigation
function collectPreorderValues(treeNode, sequenceBuffer = []) {
if (!treeNode) return sequenceBuffer;
sequenceBuffer.push(treeNode.value);
collectPreorderValues(treeNode.leftChild, sequenceBuffer);
collectPreorderValues(treeNode.rightChild, sequenceBuffer);
return sequenceBuffer;
}
Queue Mechanics
Queues implement First-In, First-Out (FIFO) processing. Insertions occur at the rear, removals at the front.
// Fundamental methods
const taskQueue = [];
taskQueue.enqueue = function(item) { this.push(item); };
taskQueue.dequeue = function() { return this.shift(); };
taskQueue.peek = function() { return this[0]; };
taskQueue.enqueue("taskA");
taskQueue.enqueue("taskB");
const completedTask = taskQueue.dequeue(); // Returns "taskA"
Typical Use Cases: Event loop management, breadth-first search, rate limiting, request buffering.
Tracking Recent Activity Windows
class ActivityTracker {
constructor() {
this.requestLog = [];
}
recordTimestamp(timestamp) {
this.requestLog.push(timestamp);
while (this.requestLog[0] < timestamp - 3000) {
this.requestLog.shift();
}
return this.requestLog.length;
}
}
Linked List Architecture
Unlike contiguous arrays, linked lists store elements dynamically across memory addresses, connected via reference pointers.
// Building a chain
let nodeAlpha = { value: "A", next: null };
let nodeBeta = { value: "B", next: null };
let nodeGamma = { value: "C", next: null };
nodeAlpha.next = nodeBeta;
nodeBeta.next = nodeGamma;
// Iteration
let current = nodeAlpha;
while (current) {
console.log(current.value);
current = current.next;
}
Advantage: Middle insertions/deletions require only pointer reassignment, avoiding costly element shifting.
Node Deletion Shortcut
// When given direct access to the target node (not tail)
function bypassNode(target) {
target.value = target.next.value;
target.next = target.next.next;
}
Reversing Chain Direction
function reverseChain(listHead) {
let previous = null;
let current = listHead;
while (current) {
const nextStep = current.next;
current.next = previous;
previous = current;
current = nextStep;
}
return previous;
}
Arithmetic Across Chains
function addChainNumbers(firstChain, secondChain) {
const resultHeader = { value: 0, next: null };
let currentNode = resultHeader;
let carryOver = 0;
while (firstChain || secondChain || carryOver) {
const val1 = firstChain ? firstChain.value : 0;
const val2 = secondChain ? secondChain.value : 0;
const sum = val1 + val2 + carryOver;
carryOver = Math.floor(sum / 10);
currentNode.next = { value: sum % 10, next: null };
currentNode = currentNode.next;
if (firstChain) firstChain = firstChain.next;
if (secondChain) secondChain = secondChain.next;
}
return resultHeader.next;
}
Eliminating Redundant Entries
function filterDuplicates(headReference) {
let tracker = headReference;
while (tracker && tracker.next) {
if (tracker.value === tracker.next.value) {
tracker.next = tracker.next.next;
} else {
tracker = tracker.next;
}
}
return headReference;
}
Cycle Detection
function detectCircularReference(headReference) {
let slowPointer = headReference;
let fastPointer = headReference;
while (fastPointer && fastPointer.next) {
slowPointer = slowPointer.next;
fastPointer = fastPointer.next.next;
if (slowPointer === fastPointer) return true;
}
return false;
}
Set Collections
Sets guarantee uniqueness and unordered storage. JavaScript provides native support via the Set constructor.
// Deduplication utility
const rawData = [7, 7, 8, 9, 8];
const uniqueItems = [...new Set(rawData)];
// Membership verification
const itemRegistry = new Set([10, 20, 30]);
console.log(itemRegistry.has(20)); // true
// Intersection calculation
const subsetA = new Set([1, 2, 3, 4]);
const subsetB = new Set([3, 4, 5]);
const sharedElements = [...subsetA].filter(val => subsetB.has(val));
Merging Collection Boundaries
function findSharedArrays(arrayPrimary, arraySecondary) {
const primarySet = new Set(arrayPrimary);
const secondarySet = new Set(arraySecondary);
const intersection = [...primarySet].filter(element => secondarySet.has(element));
return [...new Set(intersection)];
}
Map & Dictionary Structures
Dictionaries associate unique keys with corresponding values. ES6 introduces Map for efficient O(1) lookups and ordered iteration.
const configurationMap = new Map();
configurationMap.set("host", "localhost");
configurationMap.set("port", 8080);
configurationMap.delete("port");
configurationMap.set("host", "production.server.com");
Pair Summation Lookup
function locateSumIndices(numberArray, targetValue) {
const valueIndexMap = new Map();
for (let currentIndex = 0; currentIndex < numberArray.length; currentIndex++) {
const complement = targetValue - numberArray[currentIndex];
if (valueIndexMap.has(complement)) {
return [valueIndexMap.get(complement), currentIndex];
}
valueIndexMap.set(numberArray[currentIndex], currentIndex);
}
return [];
}
Window Expansion Optimization
function extractLongestUniqueSubstring(inputText) {
let windowStart = 0;
let maxSpan = 0;
const characterPositions = new Map();
for (let windowEnd = 0; windowEnd < inputText.length; windowEnd++) {
const currentChar = inputText[windowEnd];
if (characterPositions.has(currentChar) && characterPositions.get(currentChar) >= windowStart) {
windowStart = characterPositions.get(currentChar) + 1;
}
maxSpan = Math.max(maxSpan, windowEnd - windowStart + 1);
characterPositions.set(currentChar, windowEnd);
}
return maxSpan;
}
Minimal Character Coverage
function shrinkWindowTarget(mainString, requirementString) {
if (requirementString.length > mainString.length) return "";
const requiredCounts = new Map();
for (const char of requirementString) {
requiredCounts.set(char, (requiredCounts.get(char) || 0) + 1);
}
let remainingRequiredTypes = requiredCounts.size;
let leftBoundary = 0;
let optimalResult = "";
for (let rightBoundary = 0; rightBoundary < mainString.length; rightBoundary++) {
const incomingChar = mainString[rightBoundary];
if (requiredCounts.has(incomingChar)) {
requiredCounts.set(incomingChar, requiredCounts.get(incomingChar) - 1);
if (requiredCounts.get(incomingChar) === 0) remainingRequiredTypes--;
}
while (remainingRequiredTypes === 0) {
const currentWindow = mainString.substring(leftBoundary, rightBoundary + 1);
if (!optimalResult || currentWindow.length < optimalResult.length) {
optimalResult = currentWindow;
}
const outgoingChar = mainString[leftBoundary];
if (requiredCounts.has(outgoingChar)) {
requiredCounts.set(outgoingChar, requiredCounts.get(outgoingChar) + 1);
if (requiredCounts.get(outgoingChar) === 1) remainingRequiredTypes++;
}
leftBoundary++;
}
}
return optimalResult;
}
Tree Hierarchies & Traversals
Trees model hierarchical relationships. Each entity contains child references, enabling recursive exploration.
const documentStructure = {
value: "HTML",
children: [
{ value: "HEAD", children: [{ value: "TITLE", children: [] }] },
{ value: "BODY", children: [
{ value: "P", children: [] },
{ value: "DIV", children: [] }
]}
]
};
Depth-First Exploration
// Recursively descends branches fully before backtracking
function traverseDFS(currentNode) {
if (!currentNode) return;
console.log(currentNode.value);
currentNode.children.forEach(traverseDFS);
}
Breadth-First Exploration
// Processes nodes level-by-level using a queue
function traverseBFS(rootNode) {
if (!rootNode) return;
const explorationQueue = [rootNode];
while (explorationQueue.length > 0) {
const processingNode = explorationQueue.shift();
console.log(processingNode.value);
processingNode.children.forEach(childNode => explorationQueue.push(childNode));
}
}
Binarry Tree Ordered Visits
Binary structures restrict descendants to a maximum of two branches per node.
// Preorder: Root -> Left -> Right
const visitPreorder = (node) => {
if (node) {
console.log(node.val);
visitPreorder(node.left);
visitPreorder(node.right);
}
};
// Inorder: Left -> Root -> Right
const visitInorder = (node) => {
if (node) {
visitInorder(node.left);
console.log(node.val);
visitInorder(node.right);
}
};
// Postorder: Left -> Right -> Root
const visitPostorder = (node) => {
if (node) {
visitPostorder(node.left);
visitPostorder(node.right);
console.log(node.val);
}
};
Iterative Traversal Implementation
// Non-recursive Preorder
function iterativePreorder(binaryRoot) {
if (!binaryRoot) return;
const traversalStack = [binaryRoot];
while (traversalStack.length) {
const processing = traversalStack.pop();
console.log(processing.val);
if (processing.right) traversalStack.push(processing.right);
if (processing.left) traversalStack.push(processing.left);
}
}
// Non-recursive Inorder
function iterativeInorder(binaryRoot) {
if (!binaryRoot) return;
const nodeStack = [];
let pointer = binaryRoot;
while (pointer || nodeStack.length) {
while (pointer) {
nodeStack.push(pointer);
pointer = pointer.left;
}
const current = nodeStack.pop();
console.log(current.val);
pointer = current.right;
}
}
Maximum Structural Height
function determineMaxHeight(treeRoot) {
if (!treeRoot) return 0;
let deepestLevel = 0;
function exploreDepth(currentNode, currentLevel) {
if (!currentNode) return;
if (!currentNode.left && !currentNode.right) {
deepestLevel = Math.max(deepestLevel, currentLevel);
}
exploreDepth(currentNode.left, currentLevel + 1);
exploreDepth(currentNode.right, currentLevel + 1);
}
exploreDepth(treeRoot, 1);
return deepestLevel;
}
Minimum Structural Depth
function determineMinHeight(treeRoot) {
if (!treeRoot) return 0;
const bfsQueue = [[treeRoot, 1]];
while (bfsQueue.length) {
const [currentNode, depth] = bfsQueue.shift();
if (!currentNode.left && !currentNode.right) {
return depth;
}
if (currentNode.left) bfsQueue.push([currentNode.left, depth + 1]);
if (currentNode.right) bfsQueue.push([currentNode.right, depth + 1]);
}
}
Layered Node Aggregation
function groupByLevels(treeRoot) {
if (!treeRoot) return [];
const levelGroups = [];
const processingQueue = [treeRoot];
while (processingQueue.length) {
const levelSize = processingQueue.length;
const currentLevelNodes = [];
for (let i = 0; i < levelSize; i++) {
const node = processingQueue.shift();
currentLevelNodes.push(node.val);
if (node.left) processingQueue.push(node.left);
if (node.right) processingQueue.push(node.right);
}
levelGroups.push(currentLevelNodes);
}
return levelGroups;
}
Path Sum Verification
function verifyTargetPath(treeRoot, desiredSum) {
if (!treeRoot) return false;
let pathExists = false;
function tracePath(currentNode, accumulatedSum) {
if (!currentNode) return;
if (!currentNode.left && !currentNode.right && accumulatedSum === desiredSum) {
pathExists = true;
}
if (currentNode.left) tracePath(currentNode.left, accumulatedSum + currentNode.left.val);
if (currentNode.right) tracePath(currentNode.right, accumulatedSum + currentNode.right.val);
}
tracePath(treeRoot, treeRoot.val);
return pathExists;
}
Graph Networks
Graphs model complex relationships through interconnected vertices and edges. Representation typically uses adjacency matrices or lists.
// Adjacency List Format
const cityConnections = {
0: [1, 2],
1: [2],
2: [0, 3],
3: [3]
};
Water Flow Basin Simulation
function calculateFlowBasins(heightMap) {
if (!heightMap || !heightMap.length) return [];
const rows = heightMap.length;
const cols = heightMap[0].length;
const pacificAccessible = Array.from({ length: rows }, () => Array(cols).fill(false));
const atlanticAccessible = Array.from({ length: rows }, () => Array(cols).fill(false));
function floodFill(row, col, accessibleGrid) {
accessibleGrid[row][col] = true;
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const [dr, dc] of directions) {
const newRow = row + dr;
const newCol = col + dc;
if (
newRow >= 0 && newRow < rows &&
newCol >= 0 && newCol < cols &&
!accessibleGrid[newRow][newCol] &&
heightMap[newRow][newCol] >= heightMap[row][col]
) {
floodFill(newRow, newCol, accessibleGrid);
}
}
}
// Initiate flooding from ocean borders
for (let r = 0; r < rows; r++) {
floodFill(r, 0, pacificAccessible);
floodFill(r, cols - 1, atlanticAccessible);
}
for (let c = 0; c < cols; c++) {
floodFill(0, c, pacificAccessible);
floodFill(rows - 1, c, atlanticAccessible);
}
const reachableCoordinates = [];
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (pacificAccessible[r][c] && atlanticAccessible[r][c]) {
reachableCoordinates.push([r, c]);
}
}
}
return reachableCoordinates;
}
Network Replication
function duplicateNetwork(originalNode) {
if (!originalNode) return null;
const nodeMapping = new Map();
nodeMapping.set(originalNode, new Node(originalNode.val));
const expansionQueue = [originalNode];
while (expansionQueue.length) {
const source = expansionQueue.shift();
for (const neighbor of source.neighbors) {
if (!nodeMapping.has(neighbor)) {
nodeMapping.set(neighbor, new Node(neighbor.val));
expansionQueue.push(neighbor);
}
nodeMapping.get(source).neighbors.push(nodeMapping.get(neighbor));
}
}
return nodeMapping.get(originalNode);
}
Finite State Validation
function confirmNumericFormat(textualInput) {
const stateTransitions = {
0: { blank: 0, sign: 1, dot: 2, digit: 6 },
1: { digit: 6, dot: 2 },
2: { digit: 3 },
3: { digit: 3, exponent: 4 },
4: { digit: 5, sign: 7 },
5: { digit: 5 },
6: { digit: 6, dot: 3, exponent: 4 },
7: { digit: 5 }
};
let currentState = 0;
for (const char of textualInput.trim().toLowerCase()) {
let typeIdentifier;
if (char >= '0' && char <= '9') typeIdentifier = 'digit';
else if (char === ' ') typeIdentifier = 'blank';
else if (char === '+' || char === '-') typeIdentifier = 'sign';
else if (char === '.') typeIdentifier = 'dot';
else if (char === 'e') typeIdentifier = 'exponent';
else return false;
currentState = stateTransitions[currentState]?.[typeIdentifier];
if (currentState === undefined) return false;
}
return currentState === 3 || currentState === 5 || currentState === 6;
}
Priority Heaps
A heap is a specialized complete binary tree where parent nodes maintain ordering relative to their children. Min-heaps ensure the smallest value sits at the root.
Array Mapping Rules:
- Left child index:
2 * parentIndex + 1 - Right child index:
2 * parentIndex + 2 - Parent index:
(childIndex - 1) >> 1
Custom Min-Heap Implementation
class PriorityHeap {
constructor() {
this.storage = [];
}
insert(value) {
this.storage.push(value);
this.bubbleUp(this.storage.length - 1);
}
bubbleUp(index) {
if (index === 0) return;
const parentIdx = this.getParentIndex(index);
if (this.storage[parentIdx] > this.storage[index]) {
this.swap(parentIdx, index);
this.bubbleUp(parentIdx);
}
}
getParentIndex(idx) {
return (idx - 1) >> 1;
}
swap(first, second) {
[this.storage[first], this.storage[second]] = [this.storage[second], this.storage[first]];
}
extractMinimum() {
if (this.storage.length === 0) return undefined;
if (this.storage.length === 1) return this.storage.pop();
const minVal = this.storage[0];
this.storage[0] = this.storage.pop();
this.sinkDown(0);
return minVal;
}
sinkDown(index) {
let target = index;
const left = this.getLeftIndex(index);
const right = this.getRightIndex(index);
if (left < this.storage.length && this.storage[left] < this.storage[target]) {
target = left;
}
if (right < this.storage.length && this.storage[right] < this.storage[target]) {
target = right;
}
if (target !== index) {
this.swap(index, target);
this.sinkDown(target);
}
}
getLeftIndex(idx) { return idx * 2 + 1; }
getRightIndex(idx) { return idx * 2 + 2; }
retrieveMinimum() { return this.storage[0]; }
getSize() { return this.storage.length; }
}
Kth Largest Value Extraction
function retrieveKthMaximum(values, k) {
const minTracker = new PriorityHeap();
for (const num of values) {
minTracker.insert(num);
if (minTracker.getSize() > k) {
minTracker.extractMinimum();
}
}
return minTracker.retrieveMinimum();
}
Top Frequency Retrieval
// Extended comparison logic for object-based heaps
PriorityHeap.prototype.compareObjects = function(a, b) {
return a.frequency - b.frequency;
};
PriorityHeap.prototype.bubbleUpObj = function(index) {
if (index === 0) return;
const parent = this.getParentIndex(index);
if (this.compareObjects(this.storage[parent], this.storage[index]) > 0) {
this.swap(parent, index);
this.bubbleUpObj(parent);
}
};
PriorityHeap.prototype.sinkDownObj = function(index) {
let target = index;
const left = this.getLeftIndex(index);
const right = this.getRightIndex(index);
if (left < this.storage.length && this.compareObjects(this.storage[left], this.storage[target]) < 0) {
target = left;
}
if (right < this.storage.length && this.compareObjects(this.storage[right], this.storage[target]) < 0) {
target = right;
}
if (target !== index) {
this.swap(index, target);
this.sinkDownObj(target);
}
};
function fetchMostFrequentItems(dataset, limit) {
const frequencyRegistry = new Map();
for (const item of dataset) {
frequencyRegistry.set(item, (frequencyRegistry.get(item) || 0) + 1);
}
const topHeap = new PriorityHeap();
for (const [element, count] of frequencyRegistry) {
topHeap.insert({ element, frequency: count });
// Override standard swap/bubble/sink for object comparison
if (topHeap.getSize() > limit) {
topHeap.storage.shift(); // Simplified pop for object heap demonstration
topHeap.sinkDownObj(0);
}
}
return topHeap.storage.map(entry => entry.element);
}
Multi-Stream Merging
function consolidateSortedSequences(streams) {
const dummyHead = { value: null, next: null };
let cursor = dummyHead;
const activeMinHeap = new PriorityHeap();
streams.forEach(sequence => {
if (sequence) activeMinHeap.insert(sequence);
});
while (activeMinHeap.getSize() > 0) {
const selectedNode = activeMinHeap.extractMinimum();
cursor.next = selectedNode;
cursor = cursor.next;
if (selectedNode.next) {
activeMinHeap.insert(selectedNode.next);
}
}
return dummyHead.next;
}