Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

20 Commits

Repository files navigation

JavaScript Algorithms (Grouped by Type)

This repository contains a curated list of JavaScript algorithms, organized by category. These range from simple string manipulation to advanced searching and sorting techniques — perfect for interviews and foundational learning.

Note

Popularity is based on common interview topics, educational materials, and developer community usage.

Algorithms

String Manipulation

Math & Number Theory

Searching

Sorting

Array Utilities

Data Structures

Graph Algorithms

Utility Functions

String Manipulation

Reverse a String

/** * Reverses a string * @param {string} str - The string to reverse * @returns {string} The reversed string */functionreverseString(str){if(typeofstr!=="string")thrownewTypeError("Input must be a string");return[...str].reverse().join("");}console.log(reverseString("hello"));// Output: "olleh"

Explanation: Reverses the characters in a string using the spread operator (modern alternative to split) and reverse. Includes type validation.

Back to top

Palindrome Check

/** * Checks if a string is a palindrome (case-insensitive, ignores spaces) * @param {string} str - The string to check * @returns {boolean} True if palindrome, false otherwise */functionisPalindrome(str){if(typeofstr!=="string")thrownewTypeError("Input must be a string");constcleaned=str.toLowerCase().replace(/\s+/g,"");returncleaned===[...cleaned].reverse().join("");}console.log(isPalindrome("racecar"));// Output: trueconsole.log(isPalindrome("A man a plan a canal Panama"));// Output: true

Explanation: Determines if a string reads the same backward as forward. Now handles case-insensitivity and ignores spaces for real-world usage.

Back to top

Character Frequency Counter

/** * Counts the frequency of each character in a string * @param {string} str - The string to analyze * @returns {Object} An object with characters as keys and frequencies as values */functioncharFrequency(str){if(typeofstr!=="string")thrownewTypeError("Input must be a string");return[...str].reduce((freq,char)=>{freq[char]=(freq[char]??0)+1;returnfreq;},{});}console.log(charFrequency("hello"));// Output: { h: 1, e: 1, l: 2, o: 1 }

Explanation: Counts how often each character appears in a string using modern reduce() and nullish coalescing (??). More functional approach.

Back to top

Anagram Check

/** * Determines if two strings are anagrams (ignores case and spaces) * @param {string} str1 - First string * @param {string} str2 - Second string * @returns {boolean} True if anagrams, false otherwise */functionisAnagram(str1,str2){if(typeofstr1!=="string"||typeofstr2!=="string"){thrownewTypeError("Both inputs must be strings");}constnormalize=(str)=>[...str.toLowerCase().replace(/\s+/g,"")].sort().join("");returnnormalize(str1)===normalize(str2);}console.log(isAnagram("listen","silent"));// Output: trueconsole.log(isAnagram("The Eyes","They See"));// Output: true

Explanation: Determines if two strings are anagrams by normalizing case/spaces and comparing sorted characters. Handles real-world edge cases.

Back to top

Math & Number Theory

Prime Number Check

/** * Checks if a number is prime * @param {number} num - The number to check * @returns {boolean} True if prime, false otherwise */functionisPrime(num){if(!Number.isInteger(num))thrownewTypeError("Input must be an integer");if(num<=1)returnfalse;if(num<=3)returntrue;if(num%2===0||num%3===0)returnfalse;for(leti=5;i*i<=num;i+=6){if(num%i===0||num%(i+2)===0)returnfalse;}returntrue;}console.log(isPrime(7));// Output: true

Explanation: Checks if a number is prime using an optimized approach with integer validation. Eliminates multiples of 2 and 3 for efficiency.

Back to top

Fibonacci Sequence (Recursive)

/** * Generates the nth Fibonacci number using memoization * @param {number} n - The position in Fibonacci sequence * @param {Map} memo - Cache for memoization * @returns {number} The nth Fibonacci number */functionfibonacci(n,memo=newMap()){if(!Number.isInteger(n)||n<0){thrownewTypeError("Input must be a non-negative integer");}if(n<=1)returnn;if(memo.has(n))returnmemo.get(n);constresult=fibonacci(n-1,memo)+fibonacci(n-2,memo);memo.set(n,result);returnresult;}console.log(fibonacci(6));// Output: 8console.log(fibonacci(50));// Output: 12586269025 (efficient with memoization)

Explanation: Generates the nth Fibonacci number recursively with memoization for efficiency. Solves the exponential time complexity problem of naive recursion. Time complexity: O(n) instead of O(2^n).

Back to top

Factorial of a Number

/** * Calculates the factorial of a number * @param {number} n - The number to calculate factorial for * @returns {number} The factorial of n */functionfactorial(n){if(!Number.isInteger(n))thrownewTypeError("Input must be an integer");if(n<0)thrownewRangeError("Factorial is not defined for negative numbers");if(n===0||n===1)return1;returnn*factorial(n-1);}console.log(factorial(5));// Output: 120

Explanation: Calculates the factorial of a number recursively with comprehensive input validation using modern type-checking.

Back to top

Find the GCD (Greatest Common Divisor)

/** * Finds the greatest common divisor using Euclidean algorithm * @param {number} a - First number * @param {number} b - Second number * @returns {number} The GCD of a and b */functiongcd(a,b){if(!Number.isInteger(a)||!Number.isInteger(b)){thrownewTypeError("Both inputs must be integers");}a=Math.abs(a);b=Math.abs(b);returnb===0 ? a : gcd(b,a%b);}console.log(gcd(48,18));// Output: 6console.log(gcd(-48,18));// Output: 6

Explanation: Uses the Euclidean algorithm with support for negative numbers and type validation.

Back to top

Searching

Two Sum (Using Hash Map)

/** * Finds two indices in array whose values sum to target * @param {number[]} nums - Array of numbers * @param {number} target - Target sum * @returns {number[]} Array of two indices, or empty array if not found */functiontwoSum(nums,target){if(!Array.isArray(nums)||typeoftarget!=="number"){thrownewTypeError("Input must be an array and a number");}constmap=newMap();for(leti=0;i<nums.length;i++){constcomplement=target-nums[i];if(map.has(complement))return[map.get(complement),i];map.set(nums[i],i);}return[];}console.log(twoSum([2,7,11,15],9));// Output: [0, 1]

Explanation: Finds two indices whose values sum to target using a Map (hash map) for O(n) time complexity. Includes input validation.

Back to top

Binary Search

/** * Searches for target in a sorted array using binary search * @param {number[]} arr - Sorted array to search in * @param {number} target - Value to search for * @returns {number} Index of target, or -1 if not found */functionbinarySearch(arr,target){if(!Array.isArray(arr)||typeoftarget!=="number"){thrownewTypeError("Input must be an array and a number");}letleft=0,right=arr.length-1;while(left<=right){constmid=left+Math.floor((right-left)/2);if(arr[mid]===target)returnmid;if(arr[mid]<target)left=mid+1;elseright=mid-1;}return-1;}console.log(binarySearch([1,2,3,4,5],4));// Output: 3

Explanation: Searches for target in sorted array using divide-and-conquer. Uses left + Math.floor((right - left) / 2) to avoid overflow issues.

Back to top

Sorting

Bubble Sort

/** * Sorts an array using bubble sort algorithm with early exit optimization * @param {number[]} arr - Array to sort * @returns {number[]} Sorted array * Time Complexity: O(n²) worst/average case, O(n) best case * Space Complexity: O(n) */functionbubbleSort(arr){if(!Array.isArray(arr))thrownewTypeError("Input must be an array");constsorted=[...arr];// Create copy to avoid mutating originalfor(leti=0;i<sorted.length;i++){letswapped=false;// Optimization: early exit if already sortedfor(letj=0;j<sorted.length-i-1;j++){if(sorted[j]>sorted[j+1]){[sorted[j],sorted[j+1]]=[sorted[j+1],sorted[j]];swapped=true;}}if(!swapped)break;// Array is already sorted}returnsorted;}console.log(bubbleSort([5,3,8,4,2]));// Output: [2, 3, 4, 5, 8]console.log(bubbleSort([1,2,3,4,5]));// Exits early (already sorted)

Explanation: Sorts by repeatedly swapping adjacent elements. Optimized with a swapped flag to exit early when the array is already sorted, improving best-case complexity to O(n).

Back to top

Quick Sort

/** * Sorts an array using in-place quick sort algorithm * @param {number[]} arr - Array to sort * @returns {number[]} Sorted array * Time Complexity: O(n log n) average, O(n²) worst case * Space Complexity: O(log n) due to recursion stack */functionquickSort(arr){if(!Array.isArray(arr))thrownewTypeError("Input must be an array");constsorted=[...arr];// Work on copy to avoid mutationquickSortInPlace(sorted,0,sorted.length-1);returnsorted;}functionquickSortInPlace(arr,low,high){if(low<high){constpi=partition(arr,low,high);quickSortInPlace(arr,low,pi-1);quickSortInPlace(arr,pi+1,high);}}functionpartition(arr,low,high){constpivot=arr[high];leti=low-1;for(letj=low;j<high;j++){if(arr[j]<pivot){i++;[arr[i],arr[j]]=[arr[j],arr[i]];}}[arr[i+1],arr[high]]=[arr[high],arr[i+1]];returni+1;}console.log(quickSort([3,6,8,10,1,2,1]));// Output: [1, 1, 2, 3, 6, 8, 10]

Explanation: Divide-and-conquer in-place sorting with Hoare partition scheme. Uses O(log n) space instead of O(n), making it more memory-efficient. Better for interview discussions about space optimization.

Back to top

Merge Two Sorted Arrays

/** * Merges two sorted arrays into one sorted array * @param {number[]} arr1 - First sorted array * @param {number[]} arr2 - Second sorted array * @returns {number[]} Merged sorted array */functionmergeSortedArrays(arr1,arr2){if(!Array.isArray(arr1)||!Array.isArray(arr2)){thrownewTypeError("Both inputs must be arrays");}constmerged=[];leti=0,j=0;while(i<arr1.length&&j<arr2.length){if(arr1[i]<arr2[j]){merged.push(arr1[i++]);}else{merged.push(arr2[j++]);}}return[...merged, ...arr1.slice(i), ...arr2.slice(j)];}console.log(mergeSortedArrays([1,3,5],[2,4,6]));// Output: [1, 2, 3, 4, 5, 6]

Explanation: Merges two sorted arrays efficiently in O(n + m) time. Modern syntax with spread operator.

Back to top

Array Utilities

Find Maximum in Array

/** * Finds the maximum value in an array * @param {number[]} arr - Array to search * @returns {number} The maximum value */functionfindMax(arr){if(!Array.isArray(arr)||arr.length===0){thrownewTypeError("Input must be a non-empty array");}returnMath.max(...arr);}// Alternative for very large arrays (avoids stack overflow):functionfindMaxAlternative(arr){if(!Array.isArray(arr)||arr.length===0){thrownewTypeError("Input must be a non-empty array");}returnarr.reduce((max,current)=>(current>max ? current : max));}console.log(findMax([1,2,3,4,5]));// Output: 5

Explanation: Finds the largest number. Now includes validation and an alternative using reduce() for very large arrays to avoid stack overflow from spread operator.

Back to top

Utility Functions

Debounce Function

/** * Creates a debounced function that delays execution * @param {Function} fn - Function to debounce * @param {number} delay - Delay in milliseconds * @returns {Function} Debounced function */functiondebounce(fn,delay){if(typeoffn!=="function"||typeofdelay!=="number"){thrownewTypeError("First argument must be a function, second a number");}lettimerId;returnfunction(...args){clearTimeout(timerId);timerId=setTimeout(()=>fn.apply(this,args),delay);};}// Modern async version with promisesasyncfunctiondebounceAsync(fn,delay){if(typeoffn!=="function"||typeofdelay!=="number"){thrownewTypeError("First argument must be a function, second a number");}lettimerId;returnfunction(...args){returnnewPromise((resolve)=>{clearTimeout(timerId);timerId=setTimeout(()=>{resolve(fn.apply(this,args));},delay);});};}constlog=debounce(()=>console.log("Debounced!"),300);log();log();log();// Logs once after 300ms of inactivity

Explanation: Limits rate at which a function fires. Classic callback version and modern async/Promise version for modern use cases. Includes parameter validation.

Back to top

Data Structures

Linked List

/** * Node class for LinkedList */classNode{constructor(data){this.data=data;this.next=null;}}/** * Singly Linked List implementation */classLinkedList{constructor(){this.head=null;}/** * Inserts a value at the beginning * @param {*} data - Value to insert * Time Complexity: O(1) */insertAtHead(data){constnewNode=newNode(data);newNode.next=this.head;this.head=newNode;}/** * Searches for a value in the list * @param {*} data - Value to search for * @returns {boolean} True if found * Time Complexity: O(n) */search(data){letcurrent=this.head;while(current){if(current.data===data)returntrue;current=current.next;}returnfalse;}/** * Deletes first occurrence of a value * @param {*} data - Value to delete * Time Complexity: O(n) */delete(data){if(!this.head)return;if(this.head.data===data){this.head=this.head.next;return;}letcurrent=this.head;while(current.next){if(current.next.data===data){current.next=current.next.next;return;}current=current.next;}}/** * Reverses the linked list in-place * Time Complexity: O(n) */reverse(){letprev=null;letcurrent=this.head;while(current){constnext=current.next;current.next=prev;prev=current;current=next;}this.head=prev;}/** * Converts list to array for easy viewing * @returns {Array} */toArray(){constresult=[];letcurrent=this.head;while(current){result.push(current.data);current=current.next;}returnresult;}}// Usageconstlist=newLinkedList();list.insertAtHead(3);list.insertAtHead(2);list.insertAtHead(1);console.log(list.toArray());// Output: [1, 2, 3]console.log(list.search(2));// Output: truelist.reverse();console.log(list.toArray());// Output: [3, 2, 1]

Explanation: Fundamental data structure with insert, search, delete, and reverse operations. Essential for interviews to understand pointers and node traversal.

Back to top

Stack

/** * Stack implementation (Last-In-First-Out) * Time Complexity: O(1) for push/pop/peek * Space Complexity: O(n) */classStack{constructor(){this.items=[];}/** * Adds element to top of stack * @param {*} element - Value to push */push(element){this.items.push(element);}/** * Removes and returns element from top * @returns {*} Removed element or undefined */pop(){returnthis.items.length===0 ? undefined : this.items.pop();}/** * Views top element without removing * @returns {*} Top element or undefined */peek(){returnthis.items.length===0
? undefined
: this.items[this.items.length-1];}/** * Checks if stack is empty * @returns {boolean} */isEmpty(){returnthis.items.length===0;}/** * Returns size of stack * @returns {number} */size(){returnthis.items.length;}/** * Clears the stack */clear(){this.items=[];}/** * Converts stack to array * @returns {Array} */toArray(){return[...this.items];}}// Usageconststack=newStack();stack.push(10);stack.push(20);stack.push(30);console.log(stack.peek());// Output: 30console.log(stack.pop());// Output: 30console.log(stack.size());// Output: 2

Explanation: LIFO data structure critical for parsing, undo/redo, and function call management. Classic interview topic with applications in parenthesis matching and expression evaluation.

Back to top

Queue

/** * Queue implementation (First-In-First-Out) * Time Complexity: O(1) for enqueue/dequeue/peek * Space Complexity: O(n) */classQueue{constructor(){this.items=[];}/** * Adds element to back of queue * @param {*} element - Value to enqueue */enqueue(element){this.items.push(element);}/** * Removes and returns element from front * @returns {*} Removed element or undefined */dequeue(){returnthis.items.length===0 ? undefined : this.items.shift();}/** * Views front element without removing * @returns {*} Front element or undefined */peek(){returnthis.items.length===0 ? undefined : this.items[0];}/** * Checks if queue is empty * @returns {boolean} */isEmpty(){returnthis.items.length===0;}/** * Returns size of queue * @returns {number} */size(){returnthis.items.length;}/** * Clears the queue */clear(){this.items=[];}/** * Converts queue to array * @returns {Array} */toArray(){return[...this.items];}}// Usageconstqueue=newQueue();queue.enqueue(1);queue.enqueue(2);queue.enqueue(3);console.log(queue.peek());// Output: 1console.log(queue.dequeue());// Output: 1console.log(queue.size());// Output: 2

Explanation: FIFO data structure essential for BFS, task scheduling, and buffering. Note: JavaScript arrays' shift() is O(n), so for production use a circular array or linked-list implementation.

Back to top

Graph Algorithms

Depth-First Search (DFS)

/** * Depth-First Search traversal * @param {Object} graph - Adjacency list representation * @param {string|number} start - Starting vertex * @returns {Array} Order of visited vertices * Time Complexity: O(V + E) where V = vertices, E = edges * Space Complexity: O(V) for recursion stack */functiondfs(graph,start){if(!graph||!(startingraph)){thrownewTypeError("Invalid graph or start vertex");}constvisited=newSet();constresult=[];functionexplore(vertex){if(visited.has(vertex))return;visited.add(vertex);result.push(vertex);for(constneighborofgraph[vertex]){explore(neighbor);}}explore(start);returnresult;}// Iterative DFS with explicit stackfunctiondfsIterative(graph,start){if(!graph||!(startingraph)){thrownewTypeError("Invalid graph or start vertex");}constvisited=newSet();constresult=[];conststack=[start];while(stack.length>0){constvertex=stack.pop();if(!visited.has(vertex)){visited.add(vertex);result.push(vertex);// Add neighbors in reverse for left-to-right traversalfor(leti=graph[vertex].length-1;i>=0;i--){if(!visited.has(graph[vertex][i])){stack.push(graph[vertex][i]);}}}}returnresult;}// Usageconstgraph={A: ["B","C"],B: ["A","D"],C: ["A","D"],D: ["B","C","E"],E: ["D"],};console.log(dfs(graph,"A"));// Output: ["A", "B", "D", "C", "E"]console.log(dfsIterative(graph,"A"));// Output: ["A", "C", "D", "E", "B"]

Explanation: Explores graph deeply before backtracking. Recursive version is elegant; iterative version avoids stack overflow on large graphs. Used for cycle detection, topological sorting, and connected components.

Back to top

Breadth-First Search (BFS)

/** * Breadth-First Search traversal * @param {Object} graph - Adjacency list representation * @param {string|number} start - Starting vertex * @returns {Array} Order of visited vertices (level-by-level) * Time Complexity: O(V + E) where V = vertices, E = edges * Space Complexity: O(V) for queue */functionbfs(graph,start){if(!graph||!(startingraph)){thrownewTypeError("Invalid graph or start vertex");}constvisited=newSet([start]);constqueue=[start];constresult=[];while(queue.length>0){constvertex=queue.shift();result.push(vertex);for(constneighborofgraph[vertex]){if(!visited.has(neighbor)){visited.add(neighbor);queue.push(neighbor);}}}returnresult;}// BFS to find shortest pathfunctionbfsShortestPath(graph,start,end){if(!graph||!(startingraph)||!(endingraph)){thrownewTypeError("Invalid graph or vertices");}if(start===end)return[start];constvisited=newSet([start]);constqueue=[[start]];while(queue.length>0){constpath=queue.shift();constvertex=path[path.length-1];for(constneighborofgraph[vertex]){if(neighbor===end){return[...path,neighbor];}if(!visited.has(neighbor)){visited.add(neighbor);queue.push([...path,neighbor]);}}}returnnull;// No path found}// Usageconstgraph={A: ["B","C"],B: ["A","D"],C: ["A","D"],D: ["B","C","E"],E: ["D"],};console.log(bfs(graph,"A"));// Output: ["A", "B", "C", "D", "E"]console.log(bfsShortestPath(graph,"A","E"));// Output: ["A", "C", "D", "E"]

Explanation: Explores graph level-by-level, ideal for finding shortest paths and closest elements. Uses queue (FIFO). Less common than DFS for most problems but essential for shortest-path and connectivity queries.

Back to top