Skip to content

bbtools-ps/javascript-algorithms

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 

Repository files navigation

JavaScript Algorithms & Data Structures

A collection of JavaScript implementations of common algorithms and data structures. This repository includes sorting algorithms, search algorithms, data structures (trees, linked lists, heaps, queues), and various utility functions for learning and reference.

Contents

  1. Anagram Checker
  2. Binary Heaps
  3. Binary Search Tree
  4. Binary Search
  5. Cash Register
  6. Collect Odd Values
  7. Collect Strings
  8. Decimals Fix
  9. Doubly Linked List
  10. Factorial
  11. Fibonacci
  12. Fix Runts
  13. Graphs
  14. Merge Sort
  15. Palindrome Checker
  16. Priority Queue
  17. Quick Sort
  18. Radix Sort
  19. Roman Numeral Converter
  20. ROT13
  21. Segment Image
  22. Shortest Path (Dijkstra's Algorithm)
  23. Singly Linked List
  24. Stacks & Queues
  25. Telephone Checker
  26. Title Case
  27. Tree Traversal

Details

Anagram Checker

Checks if two strings are anagrams of each other. An anagram is a word or phrase formed by rearranging the letters of another word or phrase, using all the original letters exactly once. The areAnagrams function performs a case-insensitive comparison by counting character frequencies in both strings.

The algorithm works by:

  1. First checking if the strings have different lengths (quick exit if true)
  2. Converting both strings to lowercase for case-insensitive comparison
  3. Building a character frequency map for the first string
  4. Decrementing counts while iterating through the second string
  5. Verifying all counts are zero at the end
console.log(areAnagrams('cat', 'tac')); // Output: true
console.log(areAnagrams('listen', 'silent')); // Output: true
console.log(areAnagrams('hello', 'world')); // Output: false
console.log(areAnagrams('Dormitory', 'Dirty room')); // Output: false (spaces matter)

Binary Heaps

Implementation of a binary heap data structure, which is a complete binary tree where each parent node is either greater than or equal to (max heap) or less than or equal to (min heap) its child nodes. Commonly used for priority queues and heap sort algorithms.

Binary Search Tree

A binary search tree (BST) implementation where each node has at most two children, and for each node, all values in the left subtree are less than the node's value, and all values in the right subtree are greater. Provides efficient searching, insertion, and deletion operations.

Binary Search

An efficient algorithm for finding a target value within a sorted array. Works by repeatedly dividing the search interval in half, comparing the target value to the middle element, and eliminating half of the remaining elements.

console.log(binarySearch([1, 2, 3, 4, 5, 6, 7, 8, 9], 5));
// Output: 4 (index of the value)

Cash Register

A complete cash register system that manages transactions, calculates change, and tracks drawer contents. The CashRegister class uses a greedy algorithm to provide change using the largest denominations first, and handles all calculations in cents to avoid floating-point errors.

Features:

  • Transaction Processing: Validates payment, calculates change, and updates drawer
  • Change Calculation: Uses greedy algorithm considering both drawer contents and paid funds
  • Error Handling: Detects insufficient payment or inability to make proper change
  • Precision: All calculations done in cents to avoid floating-point issues
  • Flexible Denominations: Supports bills and coins (5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.01)

Implementation Details:

The class uses private methods for internal operations:

  • #addToDrawer(): Adds denominations to the drawer
  • #removeFromDrawer(): Removes denominations from the drawer
  • #convertToCents(): Converts dollar amounts to cents for precise calculations
  • #makeChange(): Calculates optimal change using greedy algorithm, considering both existing drawer contents and newly paid funds
  • #makePayment(): Adds customer payment to the drawer

Public Methods:

  • processTransaction(price, paidFunds): Process a sale and return change
  • getDrawerContents(): Get current denominations in drawer
  • getTotal(): Get total cash value in drawer
const cashRegister = new CashRegister({
  100: 5, // 5 x $100 bills
  50: 10, // 10 x $50 bills
  20: 20, // 20 x $20 bills
  10: 15,
  5: 20,
  1: 50,
  0.5: 10, // 10 x 50¢ coins
  0.25: 20, // 20 x 25¢ coins
  0.1: 50, // 50 x 10¢ coins
  0.01: 100, // 100 x 1¢ coins
});

// Process a transaction
cashRegister.processTransaction(3, { 1: 5 });
// Calculates change of $2 and updates drawer

// Exact payment
const result = cashRegister.processTransaction(20, { 20: 1 });
console.log(result);
// Output: "Exact! Here you go."

// Check drawer contents and total
const contents = cashRegister.getDrawerContents();
console.log(contents); // { 100: 5, 50: 10, ... }

const total = cashRegister.getTotal();
console.log(total); // 1234.56 (example total in dollars)

Collect Odd Values

A recursive function that collects all odd values from an array of numbers.

Collect Strings

Three different implementations for collecting all string values from a nested object. Each demonstrates a different traversal approach:

collectStringsIterative(obj) - Uses an iterative approach with a stack to traverse the object tree depth-first. This avoids recursion and potential stack overflow issues with deeply nested objects, making it suitable for large or deeply nested data structures.

collectStringsRecursiveHelper(obj) - Uses recursion with a helper function pattern. The outer function creates a result array in its scope, and an inner helper function recursively traverses the object, accumulating strings in the shared result array.

collectStringsRecursivePure(obj) - Uses pure recursion without helper functions. Each recursive call is self-contained, concatenating results from nested calls. This is a "pure" functional approach where each function call doesn't rely on outer scope variables.

const obj = {
  stuff: 'foo',
  data: {
    val: {
      thing: {
        info: 'bar',
        moreInfo: {
          evenMoreInfo: {
            weMadeIt: 'baz',
          },
        },
      },
    },
  },
};

console.log(collectStringsIterative(obj));
// Output: ["foo", "bar", "baz"]

console.log(collectStringsRecursiveHelper(obj));
// Output: ["foo", "bar", "baz"]

console.log(collectStringsRecursivePure(obj));
// Output: ["foo", "bar", "baz"]

Decimals Fix

Decimals fix overcomes Javascript floating-point rounding errors issues when trying to add or subtract two numbers with decimal spaces. For example, 0.1 + 0.2 === 0.30000000000000004. This happens because all javascript numbers are double-precision floating-point, so to fix this we have to determine how many decimals spaces there are, multiply to convert numbers to integers, perform addition or subtraction, and then divide again to get the decimal point.

let a = 0.1;
let b = 0.2;
let maxDecimals = getMaxDecimalDivider(1, Math.max(countDecimalSpaces(a), countDecimalSpaces(b)));

console.log(a + b);
// Output: 0.30000000000000004
console.log((convertDecimalsToInteger(a, 1, maxDecimals) + convertDecimalsToInteger(b, 1, maxDecimals)) / maxDecimals);
// Output: 0.3

Doubly Linked List

Implementation of a doubly linked list data structure where each node contains a reference to both the next and previous nodes in the sequence. This allows for efficient bidirectional traversal and insertion/deletion operations at both ends.

Factorial

Factorial is a function that calculates the factorial from a given number that is passed as a parameter.

console.log(factorial(10));
// Output: 3628800

Fibonacci

Two implementations for calculating the nth Fibonacci number. The Fibonacci sequence is a series where each number is the sum of the two preceding ones, starting from 1 and 1. The sequence goes: 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

fibRecursive(n) - Uses recursive approach with memoization (caching previously calculated values in a Map) to avoid redundant calculations. This makes it significantly faster than naive recursion while maintaining an elegant recursive structure. Time complexity: O(n), Space complexity: O(n)

fibTabulation(n) - Uses dynamic programming tabulation (bottom-up approach) to build the Fibonacci sequence iteratively. This approach is typically faster than the recursive memoized version for larger values as it avoids function call overhead. Time complexity: O(n), Space complexity: O(n)

// Recursive with memoization
console.log(fibRecursive(1)); // Output: 1
console.log(fibRecursive(2)); // Output: 1
console.log(fibRecursive(5)); // Output: 5
console.log(fibRecursive(10)); // Output: 55
console.log(fibRecursive(50)); // Output: 12586269025

// Tabulation (bottom-up)
console.log(fibTabulation(1)); // Output: 1
console.log(fibTabulation(2)); // Output: 1
console.log(fibTabulation(5)); // Output: 5
console.log(fibTabulation(10)); // Output: 55
console.log(fibTabulation(100)); // Output: 354224848179262000000

Fix Runts

Function that fixes runts from the string (paragraph). It replaces the last whitespace character with the   (non-breaking space character)

let str = "640K ought to be enough for anybody. (Bill Gates, 1981) The best thing about a boolean is even if you are wrong, you are only off by a bit. (Anonymous) I think Microsoft named .Net so it wouldn't show up in a Unix directory listing. (Oktal) Come to think of it, there are already a million monkeys on a million typewriters, and Usenet is nothing like Shakespeare. (Blair Houghton)"

console.log(fixRunts(str))
// Output: 640K ought to be enough for anybody. (Bill Gates, 1981) The best thing about a boolean is even if you are wrong, you are only off by a bit. (Anonymous) I think Microsoft named .Net so it wouldn't show up in a Unix directory listing. (Oktal) Come to think of it, there are already a million monkeys on a million typewriters, and Usenet is nothing like Shakespeare. (Blair Houghton)

Graphs

Implementation of an undirected graph data structure using an adjacency list representation. Includes methods for adding/removing vertices and edges, as well as traversal algorithms:

  • Depth-First Search (Recursive): Explores as far as possible along each branch before backtracking
  • Depth-First Search (Iterative): Uses a stack to traverse the graph depth-first
  • Breadth-First Search: Uses a queue to visit nodes level by level

Graphs are fundamental data structures used to represent networks, relationships, and connections between entities.

Merge Sort

A divide-and-conquer sorting algorithm that divides the input array into two halves, recursively sorts them, and then merges the sorted halves. Has a time complexity of O(n log n) and is stable.

Palindrome Checker

Checks if a string is a palindrome (reads the same forwards and backwards). A palindrome is a word or sentence that's spelled the same way both forward and backward, ignoring punctuation, case, and spacing.

The isPalindrome function extracts only alphabetic characters using regex, converts them to lowercase, and then iterates through the string comparing characters from both ends moving toward the center. If any characters don't match, it returns false; otherwise, it returns true.

console.log(isPalindrome('awesome')); // Output: false
console.log(isPalindrome('foobar')); // Output: false
console.log(isPalindrome('eye')); // Output: true
console.log(isPalindrome('A man, a plan, a canal: Panama')); // Output: true
console.log(isPalindrome('race car')); // Output: true

Priority Queue

A priority queue implementation using a binary heap where elements are served based on their priority rather than their order in the queue. Higher priority elements are dequeued before lower priority elements.

Quick Sort

An efficient divide-and-conquer sorting algorithm that works by selecting a 'pivot' element and partitioning the array around it, such that elements smaller than the pivot come before it and elements greater come after. Has an average time complexity of O(n log n).

Radix Sort

A non-comparative sorting algorithm that sorts integers by processing individual digits. It groups numbers by each digit position and is particularly efficient for sorting large sets of integers.

Roman Numeral Converter

Converts the Arabic numbers to roman and vice versa. For converting from Arabic to roman use function convertToRoman(num) with the number as a parameter and for converting from roman to Arabic use function convertToArabic(str) with the string as a parameter that is passed to the function. Both functions return a number or a letter, depending on which function you use.

console.log(convertToRoman(3999));
// Output: MMMCMXCIX
console.log(convertToArabic("MMMCMXCIX"));
// Output: 3999

ROT13

A simple letter substitution cipher that replaces a letter with the letter 13 positions after it in the alphabet. ROT13 is a special case of the Caesar cipher and is its own inverse (applying ROT13 twice returns the original text).

console.log(rot13("SERR PBQR PNZC"));
// Output: FREE CODE CAMP

Segment Image

Segments a 2D image array by finding all connected regions of a specific value using Breadth-First Search (BFS). This algorithm identifies connected components in a grid where cells are considered connected if they share an edge (4-directional connectivity: up, down, left, right).

The segmentByValue function takes a 2D array and a target value, then returns all segments where each segment is a group of connected cells with that value. This is useful for image processing tasks like blob detection, region labeling, and pattern recognition.

const img = [
  [0, 1, 0],
  [0, 1, 1],
  [1, 0, 0]
];

const segments = segmentByValue(img, 1); // Find all segments of 1s
console.log(segments);
// Output: [[[0,1], [1,1], [1,2]], [[2,0]]]
// Two separate segments: one with three connected 1s, one with a single 1

const zeroSegments = segmentByValue(img, 0); // Find all segments of 0s
console.log(zeroSegments);
// Output: [[[0,0], [1,0]], [[0,2], [1,2], [2,1], [2,2]]]

Shortest Path (Dijkstra's Algorithm)

Implementation of Dijkstra's algorithm for finding the shortest path between nodes in a weighted graph. Uses a priority queue (min-heap) to efficiently select the next node with the smallest distance. The algorithm:

  1. Initializes distances from the start node (0 for start, Infinity for others)
  2. Uses a priority queue to process nodes in order of their distance
  3. For each node, updates distances to neighbors if a shorter path is found
  4. Tracks the previous node in each shortest path to reconstruct the final route

Returns an array representing the shortest path from the start to the end node. Essential for navigation systems, network routing, and optimization problems.

Singly Linked List

Implementation of a singly linked list data structure where each node contains data and a reference to the next node in the sequence. Provides efficient insertion and deletion at the beginning, but requires traversal for accessing elements.

Stacks & Queues

Implementation of stack (Last-In-First-Out) and queue (First-In-First-Out) data structures. Stacks support push and pop operations, while queues support enqueue and dequeue operations.

Telephone Checker

Checks if a telephone number is in valid format. The parameter that is passed to the function is a string and the function returns a boolean as a result.

console.log(telephoneCheck("1 555)555-5555"));
// Output: false
console.log(telephoneCheck("1 555-555-5555"));
// Output: true

Title Case

The function that converts the string that is passed as a parameter to the title case.

let str = 'title  case here';

console.log(titleCase(str));
// Output: Title Case Here

Tree Traversal

Implementation of various tree traversal algorithms including:

  • Breadth-First Search (BFS): Visits nodes level by level
  • Depth-First Search (DFS): Includes Pre-order, In-order, and Post-order traversals

These algorithms are fundamental for navigating and processing tree data structures.

Palindrome checker

Checks if a word or a sentence is a palindrome. A palindrome is a word or a sentence that's spelled the same way both forward and backward, ignoring the punctuation, case, and spacing. The function works by removing all non-alphanumeric characters (punctuation, spaces, and symbols) and turns everything into the same case (lower or upper case) to check for palindromes. The result of the function is a boolean (true or false) and it depends if the palindrome is found or not.

console.log(palindrome("1 eye for of 1 eye."));
// Output: true

Factorial

Factorial is a function that calculates the factorial from a given number that is passed as a parameter.

console.log(factorial(10));
// Output: 3628800

Telephone checker

Checks if a telephone number is in valid format. The parameter that is passed to the function is a string and the function returns a boolean as a result.

console.log(telephoneCheck("1 555)555-5555"));
// Output: false
console.log(telephoneCheck("1 555-555-5555"));
// Output: true

Decimals fix

Decimals fix overcomes Javascript floating-point rounding errors issues when trying to add or subtract two numbers with decimal spaces. For example, 0.1 + 0.2 === 0.30000000000000004. This happens because all javascript numbers are double-precision floating-point, so to fix this we have to determine how many decimals spaces there are, multiply to convert numbers to integers, perform addition or subtraction, and then divide again to get the decimal point.

let a = 0.1;
let b = 0.2;
let maxDecimals = getMaxDecimalDivider(1, Math.max(countDecimalSpaces(a), countDecimalSpaces(b)));

console.log(a + b);
// Output: 0.30000000000000004
console.log((convertDecimalsToInteger(a, 1, maxDecimals) + convertDecimalsToInteger(b, 1, maxDecimals)) / maxDecimals);
// Output: 0.3

Title case

The function that converts the string that is passed as a parameter to the title case.

let str = 'title  case here';

console.log(titleCase(str));
// Output: Title Case Here

Fix runts

Function that fixes runts from the string (paragraph). It replaces the last whitespace character with the   (non-breaking space character)

let str = "640K ought to be enough for anybody. (Bill Gates, 1981) The best thing about a boolean is even if you are wrong, you are only off by a bit. (Anonymous) I think Microsoft named .Net so it wouldn’t show up in a Unix directory listing. (Oktal) Come to think of it, there are already a million monkeys on a million typewriters, and Usenet is nothing like Shakespeare. (Blair Houghton)"

console.log(fixRunts(str))
// Output: 640K ought to be enough for anybody. (Bill Gates, 1981) The best thing about a boolean is even if you are wrong, you are only off by a bit. (Anonymous) I think Microsoft named .Net so it wouldn’t show up in a Unix directory listing. (Oktal) Come to think of it, there are already a million monkeys on a million typewriters, and Usenet is nothing like Shakespeare. (Blair Houghton)

About

Javascript practice projects.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published