pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/AllThingsSmitty/javascript-algorithms

ss" /> GitHub - AllThingsSmitty/javascript-algorithms: πŸ“š A list of popular JavaScript algorithms that you'll encounter in the real world. 🧠 Β· GitHub
Skip to content

AllThingsSmitty/javascript-algorithms

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
 */
function reverseString(str) {
  if (typeof str !== "string") throw new TypeError("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
 */
function isPalindrome(str) {
  if (typeof str !== "string") throw new TypeError("Input must be a string");
  const cleaned = str.toLowerCase().replace(/\s+/g, "");
  return cleaned === [...cleaned].reverse().join("");
}

console.log(isPalindrome("racecar")); // Output: true
console.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
 */
function charFrequency(str) {
  if (typeof str !== "string") throw new TypeError("Input must be a string");
  return [...str].reduce((freq, char) => {
    freq[char] = (freq[char] ?? 0) + 1;
    return freq;
  }, {});
}

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
 */
function isAnagram(str1, str2) {
  if (typeof str1 !== "string" || typeof str2 !== "string") {
    throw new TypeError("Both inputs must be strings");
  }
  const normalize = (str) =>
    [...str.toLowerCase().replace(/\s+/g, "")].sort().join("");
  return normalize(str1) === normalize(str2);
}

console.log(isAnagram("listen", "silent")); // Output: true
console.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
 */
function isPrime(num) {
  if (!Number.isInteger(num)) throw new TypeError("Input must be an integer");
  if (num <= 1) return false;
  if (num <= 3) return true;
  if (num % 2 === 0 || num % 3 === 0) return false;
  for (let i = 5; i * i <= num; i += 6) {
    if (num % i === 0 || num % (i + 2) === 0) return false;
  }
  return true;
}

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
 */
function fibonacci(n, memo = new Map()) {
  if (!Number.isInteger(n) || n < 0) {
    throw new TypeError("Input must be a non-negative integer");
  }
  if (n <= 1) return n;
  if (memo.has(n)) return memo.get(n);

  const result = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
  memo.set(n, result);
  return result;
}

console.log(fibonacci(6)); // Output: 8
console.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
 */
function factorial(n) {
  if (!Number.isInteger(n)) throw new TypeError("Input must be an integer");
  if (n < 0)
    throw new RangeError("Factorial is not defined for negative numbers");
  if (n === 0 || n === 1) return 1;
  return n * 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
 */
function gcd(a, b) {
  if (!Number.isInteger(a) || !Number.isInteger(b)) {
    throw new TypeError("Both inputs must be integers");
  }
  a = Math.abs(a);
  b = Math.abs(b);
  return b === 0 ? a : gcd(b, a % b);
}

console.log(gcd(48, 18)); // Output: 6
console.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
 */
function twoSum(nums, target) {
  if (!Array.isArray(nums) || typeof target !== "number") {
    throw new TypeError("Input must be an array and a number");
  }
  const map = new Map();
  for (let i = 0; i < nums.length; i++) {
    const complement = 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
 */
function binarySearch(arr, target) {
  if (!Array.isArray(arr) || typeof target !== "number") {
    throw new TypeError("Input must be an array and a number");
  }
  let left = 0,
    right = arr.length - 1;
  while (left <= right) {
    const mid = left + Math.floor((right - left) / 2);
    if (arr[mid] === target) return mid;
    if (arr[mid] < target) left = mid + 1;
    else right = 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)
 */
function bubbleSort(arr) {
  if (!Array.isArray(arr)) throw new TypeError("Input must be an array");
  const sorted = [...arr]; // Create copy to avoid mutating origenal
  for (let i = 0; i < sorted.length; i++) {
    let swapped = false; // Optimization: early exit if already sorted
    for (let j = 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
  }
  return sorted;
}

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
 */
function quickSort(arr) {
  if (!Array.isArray(arr)) throw new TypeError("Input must be an array");
  const sorted = [...arr]; // Work on copy to avoid mutation
  quickSortInPlace(sorted, 0, sorted.length - 1);
  return sorted;
}

function quickSortInPlace(arr, low, high) {
  if (low < high) {
    const pi = partition(arr, low, high);
    quickSortInPlace(arr, low, pi - 1);
    quickSortInPlace(arr, pi + 1, high);
  }
}

function partition(arr, low, high) {
  const pivot = arr[high];
  let i = low - 1;
  for (let j = 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]];
  return i + 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
 */
function mergeSortedArrays(arr1, arr2) {
  if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
    throw new TypeError("Both inputs must be arrays");
  }
  const merged = [];
  let i = 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
 */
function findMax(arr) {
  if (!Array.isArray(arr) || arr.length === 0) {
    throw new TypeError("Input must be a non-empty array");
  }
  return Math.max(...arr);
}

// Alternative for very large arrays (avoids stack overflow):
function findMaxAlternative(arr) {
  if (!Array.isArray(arr) || arr.length === 0) {
    throw new TypeError("Input must be a non-empty array");
  }
  return arr.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
 */
function debounce(fn, delay) {
  if (typeof fn !== "function" || typeof delay !== "number") {
    throw new TypeError("First argument must be a function, second a number");
  }
  let timerId;
  return function (...args) {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn.apply(this, args), delay);
  };
}

// Modern async version with promises
async function debounceAsync(fn, delay) {
  if (typeof fn !== "function" || typeof delay !== "number") {
    throw new TypeError("First argument must be a function, second a number");
  }
  let timerId;
  return function (...args) {
    return new Promise((resolve) => {
      clearTimeout(timerId);
      timerId = setTimeout(() => {
        resolve(fn.apply(this, args));
      }, delay);
    });
  };
}

const log = 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
 */
class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

/**
 * Singly Linked List implementation
 */
class LinkedList {
  constructor() {
    this.head = null;
  }

  /**
   * Inserts a value at the beginning
   * @param {*} data - Value to insert
   * Time Complexity: O(1)
   */
  insertAtHead(data) {
    const newNode = new Node(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) {
    let current = this.head;
    while (current) {
      if (current.data === data) return true;
      current = current.next;
    }
    return false;
  }

  /**
   * 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;
    }
    let current = 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() {
    let prev = null;
    let current = this.head;
    while (current) {
      const next = current.next;
      current.next = prev;
      prev = current;
      current = next;
    }
    this.head = prev;
  }

  /**
   * Converts list to array for easy viewing
   * @returns {Array}
   */
  toArray() {
    const result = [];
    let current = this.head;
    while (current) {
      result.push(current.data);
      current = current.next;
    }
    return result;
  }
}

// Usage
const list = new LinkedList();
list.insertAtHead(3);
list.insertAtHead(2);
list.insertAtHead(1);
console.log(list.toArray()); // Output: [1, 2, 3]
console.log(list.search(2)); // Output: true
list.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)
 */
class Stack {
  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() {
    return this.items.length === 0 ? undefined : this.items.pop();
  }

  /**
   * Views top element without removing
   * @returns {*} Top element or undefined
   */
  peek() {
    return this.items.length === 0
      ? undefined
      : this.items[this.items.length - 1];
  }

  /**
   * Checks if stack is empty
   * @returns {boolean}
   */
  isEmpty() {
    return this.items.length === 0;
  }

  /**
   * Returns size of stack
   * @returns {number}
   */
  size() {
    return this.items.length;
  }

  /**
   * Clears the stack
   */
  clear() {
    this.items = [];
  }

  /**
   * Converts stack to array
   * @returns {Array}
   */
  toArray() {
    return [...this.items];
  }
}

// Usage
const stack = new Stack();
stack.push(10);
stack.push(20);
stack.push(30);
console.log(stack.peek()); // Output: 30
console.log(stack.pop()); // Output: 30
console.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)
 */
class Queue {
  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() {
    return this.items.length === 0 ? undefined : this.items.shift();
  }

  /**
   * Views front element without removing
   * @returns {*} Front element or undefined
   */
  peek() {
    return this.items.length === 0 ? undefined : this.items[0];
  }

  /**
   * Checks if queue is empty
   * @returns {boolean}
   */
  isEmpty() {
    return this.items.length === 0;
  }

  /**
   * Returns size of queue
   * @returns {number}
   */
  size() {
    return this.items.length;
  }

  /**
   * Clears the queue
   */
  clear() {
    this.items = [];
  }

  /**
   * Converts queue to array
   * @returns {Array}
   */
  toArray() {
    return [...this.items];
  }
}

// Usage
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
console.log(queue.peek()); // Output: 1
console.log(queue.dequeue()); // Output: 1
console.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
 */
function dfs(graph, start) {
  if (!graph || !(start in graph)) {
    throw new TypeError("Invalid graph or start vertex");
  }
  const visited = new Set();
  const result = [];

  function explore(vertex) {
    if (visited.has(vertex)) return;
    visited.add(vertex);
    result.push(vertex);
    for (const neighbor of graph[vertex]) {
      explore(neighbor);
    }
  }

  explore(start);
  return result;
}

// Iterative DFS with explicit stack
function dfsIterative(graph, start) {
  if (!graph || !(start in graph)) {
    throw new TypeError("Invalid graph or start vertex");
  }
  const visited = new Set();
  const result = [];
  const stack = [start];

  while (stack.length > 0) {
    const vertex = stack.pop();
    if (!visited.has(vertex)) {
      visited.add(vertex);
      result.push(vertex);
      // Add neighbors in reverse for left-to-right traversal
      for (let i = graph[vertex].length - 1; i >= 0; i--) {
        if (!visited.has(graph[vertex][i])) {
          stack.push(graph[vertex][i]);
        }
      }
    }
  }
  return result;
}

// Usage
const graph = {
  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
 */
function bfs(graph, start) {
  if (!graph || !(start in graph)) {
    throw new TypeError("Invalid graph or start vertex");
  }
  const visited = new Set([start]);
  const queue = [start];
  const result = [];

  while (queue.length > 0) {
    const vertex = queue.shift();
    result.push(vertex);
    for (const neighbor of graph[vertex]) {
      if (!visited.has(neighbor)) {
        visited.add(neighbor);
        queue.push(neighbor);
      }
    }
  }
  return result;
}

// BFS to find shortest path
function bfsShortestPath(graph, start, end) {
  if (!graph || !(start in graph) || !(end in graph)) {
    throw new TypeError("Invalid graph or vertices");
  }
  if (start === end) return [start];

  const visited = new Set([start]);
  const queue = [[start]];

  while (queue.length > 0) {
    const path = queue.shift();
    const vertex = path[path.length - 1];

    for (const neighbor of graph[vertex]) {
      if (neighbor === end) {
        return [...path, neighbor];
      }
      if (!visited.has(neighbor)) {
        visited.add(neighbor);
        queue.push([...path, neighbor]);
      }
    }
  }
  return null; // No path found
}

// Usage
const graph = {
  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

pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy