├── .babelrc ├── src ├── playground │ ├── playground.js │ ├── __test__ │ │ └── playground.test.js │ └── README.md ├── algorithms │ ├── math │ │ ├── bits │ │ │ ├── divideByTwo.js │ │ │ ├── multiplyByTwo.js │ │ │ ├── getBit.js │ │ │ ├── setBit.js │ │ │ ├── switchSign.js │ │ │ ├── clearBit.js │ │ │ ├── __test__ │ │ │ │ ├── divideByTwo.test.js │ │ │ │ ├── setBit.test.js │ │ │ │ ├── clearBit.test.js │ │ │ │ ├── multiplyByTwo.test.js │ │ │ │ ├── switchSign.test.js │ │ │ │ ├── getBit.test.js │ │ │ │ └── updateBit.test.js │ │ │ └── updateBit.js │ │ ├── factorial │ │ │ ├── factorialRecursive.js │ │ │ ├── factorial.js │ │ │ ├── __test__ │ │ │ │ ├── factorial.test.js │ │ │ │ └── factorialRecursive.test.js │ │ │ └── README.md │ │ ├── euclidean-algorithm │ │ │ ├── euclideanAlgorithm.js │ │ │ └── __test__ │ │ │ │ └── euclieanAlgorithm.test.js │ │ ├── least-common-multiple │ │ │ ├── leastCommonMultiple.js │ │ │ └── __test__ │ │ │ │ └── leastCommonMultiple.test.js │ │ ├── pascal-triangle │ │ │ ├── pascalTriangle.js │ │ │ ├── __test__ │ │ │ │ ├── pascalTriangle.test.js │ │ │ │ └── pascalTriangleRecursive.test.js │ │ │ └── pascalTriangleRecursive.js │ │ ├── sieve-of-eratosthenes │ │ │ ├── __test__ │ │ │ │ └── sieveOfEratosthenes.test.js │ │ │ ├── sieveOfEratosthenes.js │ │ │ └── README.md │ │ ├── fibonacci │ │ │ ├── __test__ │ │ │ │ ├── fibonacciNth.test.js │ │ │ │ └── fibonacci.test.js │ │ │ ├── fibonacciNth.js │ │ │ ├── fibonacci.js │ │ │ └── README.md │ │ ├── integer-partition │ │ │ ├── __test__ │ │ │ │ └── integerPartition.test.js │ │ │ └── README.md │ │ ├── liu-hui │ │ │ ├── __test__ │ │ │ │ └── liuHui.test.js │ │ │ └── liuHui.js │ │ ├── is-power-of-two │ │ │ ├── isPowerOfTwo.js │ │ │ ├── isPowerOfTwoBitwise.js │ │ │ ├── __test__ │ │ │ │ ├── isPowerOfTwo.test.js │ │ │ │ └── isPowerOfTwoBitwise.test.js │ │ │ └── README.md │ │ └── primality-test │ │ │ ├── trialDivision.js │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ └── trialDivision.test.js │ ├── sorting │ │ ├── __test__ │ │ │ └── Sort.test.js │ │ ├── heap-sort │ │ │ ├── HeapSort.js │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ └── HeapSort.test.js │ │ ├── radix-sort │ │ │ └── __test__ │ │ │ │ └── RadixSort.test.js │ │ ├── insertion-sort │ │ │ ├── README.md │ │ │ └── InsertionSort.js │ │ ├── selection-sort │ │ │ ├── SelectionSort.js │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ └── SelectionSort.test.js │ │ ├── bubble-sort │ │ │ ├── README.md │ │ │ ├── BubbleSort.js │ │ │ └── __test__ │ │ │ │ └── BubbleSort.test.js │ │ ├── Sort.js │ │ ├── shell-sort │ │ │ ├── ShellSort.js │ │ │ └── __test__ │ │ │ │ └── ShellSort.test.js │ │ ├── quick-sort │ │ │ ├── QuickSort.js │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ ├── QuickSortInPlace.test.js │ │ │ │ └── QuickSort.test.js │ │ └── merge-sort │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ └── MergeSort.test.js │ ├── sets │ │ ├── power-set │ │ │ ├── README.md │ │ │ ├── __test__ │ │ │ │ └── powerSet.test.js │ │ │ └── powerSet.js │ │ ├── cartesian-product │ │ │ ├── cartesianProduct.js │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ └── cartesianProduct.test.js │ │ ├── fisher-yates │ │ │ ├── fisherYates.js │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ └── fisherYates.test.js │ │ ├── combination-sum │ │ │ ├── __test__ │ │ │ │ └── combinationSum.test.js │ │ │ └── README.md │ │ ├── maximum-subarray │ │ │ ├── __test__ │ │ │ │ ├── bfMaximumSubarray.test.js │ │ │ │ └── dpMaximumSubarray.test.js │ │ │ ├── bfMaximumSubarray.js │ │ │ └── README.md │ │ ├── shortest-common-supersequence │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ └── shortestCommonSupersequence.test.js │ │ ├── combinations │ │ │ ├── combineWithRepetitions.js │ │ │ ├── combineWithoutRepetitions.js │ │ │ └── __test__ │ │ │ │ └── combineWithRepetitions.test.js │ │ ├── permutations │ │ │ ├── permutateWithRepetitions.js │ │ │ ├── permutateWithoutRepetitions.js │ │ │ ├── __test__ │ │ │ │ └── permutateWithRepetitions.test.js │ │ │ └── README.md │ │ ├── knapsack-problem │ │ │ ├── KnapsackItem.js │ │ │ └── __test__ │ │ │ │ └── KnapsackItem.test.js │ │ ├── longest-increasing-subsequence │ │ │ ├── __test__ │ │ │ │ └── dpLongestIncreasingSubsequence.test.js │ │ │ └── README.md │ │ └── longest-common-subsequence │ │ │ ├── __test__ │ │ │ └── longestCommonSubsequence.test.js │ │ │ └── README.md │ ├── uncategorized │ │ ├── unique-paths │ │ │ ├── uniquePaths.js │ │ │ ├── __test__ │ │ │ │ ├── uniquePaths.test.js │ │ │ │ ├── btUniquePaths.test.js │ │ │ │ └── dpUniquePaths.test.js │ │ │ └── dpUniquePaths.js │ │ ├── n-queens │ │ │ ├── __test__ │ │ │ │ ├── QueensPosition.test.js │ │ │ │ └── nQueens.test.js │ │ │ └── QueenPosition.js │ │ ├── jump-game │ │ │ ├── __test__ │ │ │ │ ├── greedyJumpGame.test.js │ │ │ │ ├── dpTopDownJumpGame.test.js │ │ │ │ ├── dpBottomUpJumpGame.test.js │ │ │ │ └── backtrackingJumpGame.test.js │ │ │ └── backtrackingJumpGame.js │ │ ├── knight-tour │ │ │ ├── __test__ │ │ │ │ └── knightTour.test.js │ │ │ └── README.md │ │ ├── square-matrix-rotation │ │ │ ├── squareMatrixRotation.js │ │ │ └── __test__ │ │ │ │ └── squareMatrixRotation.test.js │ │ └── hanoi-tower │ │ │ └── README.md │ ├── string │ │ ├── hamming-distance │ │ │ ├── hammingDistance.js │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ └── hammingDistance.test.js │ │ ├── longest-common-substring │ │ │ ├── __test__ │ │ │ │ └── longestCommonSubstring.test.js │ │ │ └── README.md │ │ ├── knuth-morris-pratt │ │ │ ├── README.md │ │ │ ├── __test__ │ │ │ │ └── knuthMorrisPratt.test.js │ │ │ └── knuthMorrisPratt.js │ │ ├── z-algorithm │ │ │ └── __test__ │ │ │ │ └── zAlgorithm.test.js │ │ ├── rabin-karp │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ └── rabinKarp.test.js │ │ ├── levenshtein-distance │ │ │ ├── __test__ │ │ │ │ └── levenshteinDistance.test.js │ │ │ └── levenshteinDistance.js │ │ └── regular-expression-matching │ │ │ └── README.md │ ├── search │ │ ├── linear-search │ │ │ ├── linearSearch.js │ │ │ ├── README.md │ │ │ └── __test__ │ │ │ │ └── linearSearch.test.js │ │ ├── binary-search │ │ │ ├── README.md │ │ │ ├── binarySearch.js │ │ │ └── __test__ │ │ │ │ └── binarySearch.test.js │ │ ├── jump-search │ │ │ ├── README.md │ │ │ ├── jumpSearch.js │ │ │ └── __test__ │ │ │ │ └── jumpSearch.test.js │ │ └── interpolation-search │ │ │ ├── __test__ │ │ │ └── interpolationSearch.test.js │ │ │ └── README.md │ ├── graph │ │ ├── depth-first-search │ │ │ └── README.md │ │ ├── strongly-connected-components │ │ │ └── README.md │ │ ├── breadth-first-search │ │ │ └── README.md │ │ ├── bellman-ford │ │ │ ├── README.md │ │ │ └── bellmanFord.js │ │ ├── articulation-points │ │ │ └── README.md │ │ ├── dijkstra │ │ │ └── README.md │ │ ├── bridges │ │ │ └── README.md │ │ ├── travelling-salesman │ │ │ └── README.md │ │ ├── detect-cycle │ │ │ ├── detectUndirectedCycleUsingDisjointSet.js │ │ │ └── __test__ │ │ │ │ ├── detectUndirectedCycleUsingDisjointSet.test.js │ │ │ │ ├── detectUndirectedCycle.test.js │ │ │ │ └── detectDirectedCycle.test.js │ │ ├── topological-sorting │ │ │ └── topologicalSort.js │ │ └── eulerian-path │ │ │ └── README.md │ └── tree │ │ ├── depth-first-search │ │ ├── README.md │ │ └── depthFirstSearch.js │ │ └── breadth-first-search │ │ └── README.md ├── data-structures │ ├── linked-list │ │ ├── LinkedListNode.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── LinkedListNode.test.js │ ├── doubly-linked-list │ │ ├── DoublyLinkedListNode.js │ │ └── README.md │ ├── queue │ │ ├── Queue.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── Queue.test.js │ ├── heap │ │ └── README.md │ ├── graph │ │ ├── GraphEdge.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── GraphEdge.test.js │ ├── priority-queue │ │ └── README.md │ ├── tree │ │ ├── binary-search-tree │ │ │ ├── BinarySearchTree.js │ │ │ └── README.md │ │ └── README.md │ ├── trie │ │ ├── TrieNode.js │ │ ├── README.md │ │ ├── Trie.js │ │ └── __test__ │ │ │ ├── TrieNode.test.js │ │ │ └── Trie.test.js │ ├── hash-table │ │ └── README.md │ ├── disjoint-set │ │ └── README.md │ └── stack │ │ ├── Stack.js │ │ └── README.md └── utils │ └── comparator │ └── Comparator.js ├── .gitignore ├── assets └── big-o-graph.png ├── .editorconfig ├── .travis.yml ├── .eslintrc ├── CONTRIBUTING.md ├── jest.config.js ├── LICENSE └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /src/playground/playground.js: -------------------------------------------------------------------------------- 1 | // Place your playground code here. 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | coverage 4 | .vscode 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /assets/big-o-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/picasso999/javascript-algorithms/HEAD/assets/big-o-graph.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/divideByTwo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @return {number} 4 | */ 5 | export default function divideByTwo(number) { 6 | return number >> 1; 7 | } 8 | -------------------------------------------------------------------------------- /src/playground/__test__/playground.test.js: -------------------------------------------------------------------------------- 1 | describe('playground', () => { 2 | it('should perform playground tasks', () => { 3 | // Place your playground tests here. 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/multiplyByTwo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @return {number} 4 | */ 5 | export default function multiplyByTwo(number) { 6 | return number << 1; 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: node_js 4 | node_js: 5 | - node 6 | script: 7 | - npm run ci 8 | - npm run codecov 9 | notifications: 10 | email: false 11 | -------------------------------------------------------------------------------- /src/algorithms/math/factorial/factorialRecursive.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @return {number} 4 | */ 5 | export default function factorialRecursive(number) { 6 | return number > 1 ? number * factorialRecursive(number - 1) : 1; 7 | } 8 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/getBit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @param {number} bitPosition - zero based. 4 | * @return {number} 5 | */ 6 | export default function getBit(number, bitPosition) { 7 | return (number >> bitPosition) & 1; 8 | } 9 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/setBit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @param {number} bitPosition - zero based. 4 | * @return {number} 5 | */ 6 | export default function setBit(number, bitPosition) { 7 | return number | (1 << bitPosition); 8 | } 9 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/switchSign.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Switch the sign of the number using "Twos Complement" approach. 3 | * @param {number} number 4 | * @return {number} 5 | */ 6 | export default function switchSign(number) { 7 | return ~number + 1; 8 | } 9 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/clearBit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @param {number} bitPosition - zero based. 4 | * @return {number} 5 | */ 6 | export default function clearBit(number, bitPosition) { 7 | const mask = ~(1 << bitPosition); 8 | 9 | return number & mask; 10 | } 11 | -------------------------------------------------------------------------------- /src/algorithms/math/factorial/factorial.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @return {number} 4 | */ 5 | export default function factorial(number) { 6 | let result = 1; 7 | 8 | for (let i = 2; i <= number; i += 1) { 9 | result *= i; 10 | } 11 | 12 | return result; 13 | } 14 | -------------------------------------------------------------------------------- /src/data-structures/linked-list/LinkedListNode.js: -------------------------------------------------------------------------------- 1 | export default class LinkedListNode { 2 | constructor(value, next = null) { 3 | this.value = value; 4 | this.next = next; 5 | } 6 | 7 | toString(callback) { 8 | return callback ? callback(this.value) : `${this.value}`; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/playground/README.md: -------------------------------------------------------------------------------- 1 | # Playground 2 | 3 | You may use `playground.js` file to play with data 4 | structures and algorithms. The code from `playground.js` may 5 | be tested in `./__test__/playground.test.js` file. 6 | 7 | To run tests simply run: 8 | 9 | ``` 10 | npm test -- -t 'playground' 11 | ``` 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "airbnb", 4 | "plugins": ["jest"], 5 | "env": { 6 | "jest/globals": true 7 | }, 8 | "rules": { 9 | "no-bitwise": "off", 10 | "no-lonely-if": "off", 11 | "class-methods-use-this": "off", 12 | "arrow-body-style": "off", 13 | "no-loop-func": "off" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/algorithms/sorting/__test__/Sort.test.js: -------------------------------------------------------------------------------- 1 | import Sort from '../Sort'; 2 | 3 | describe('Sort', () => { 4 | it('should throw an error when trying to call Sort.sort() method directly', () => { 5 | function doForbiddenSort() { 6 | const sorter = new Sort(); 7 | sorter.sort(); 8 | } 9 | 10 | expect(doForbiddenSort).toThrow(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/data-structures/doubly-linked-list/DoublyLinkedListNode.js: -------------------------------------------------------------------------------- 1 | export default class DoublyLinkedListNode { 2 | constructor(value, next = null, previous = null) { 3 | this.value = value; 4 | this.next = next; 5 | this.previous = previous; 6 | } 7 | 8 | toString(callback) { 9 | return callback ? callback(this.value) : `${this.value}`; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} originalA 3 | * @param {number} originalB 4 | * @return {number} 5 | */ 6 | export default function euclideanAlgorithm(originalA, originalB) { 7 | const a = Math.abs(originalA); 8 | const b = Math.abs(originalB); 9 | 10 | return (b === 0) ? a : euclideanAlgorithm(b, a % b); 11 | } 12 | -------------------------------------------------------------------------------- /src/algorithms/math/least-common-multiple/leastCommonMultiple.js: -------------------------------------------------------------------------------- 1 | import euclideanAlgorithm from '../euclidean-algorithm/euclideanAlgorithm'; 2 | 3 | /** 4 | * @param {number} a 5 | * @param {number} b 6 | * @return {number} 7 | */ 8 | 9 | export default function leastCommonMultiple(a, b) { 10 | return ((a === 0) || (b === 0)) ? 0 : Math.abs(a * b) / euclideanAlgorithm(a, b); 11 | } 12 | -------------------------------------------------------------------------------- /src/algorithms/math/factorial/__test__/factorial.test.js: -------------------------------------------------------------------------------- 1 | import factorial from '../factorial'; 2 | 3 | describe('factorial', () => { 4 | it('should calculate factorial', () => { 5 | expect(factorial(0)).toBe(1); 6 | expect(factorial(1)).toBe(1); 7 | expect(factorial(5)).toBe(120); 8 | expect(factorial(8)).toBe(40320); 9 | expect(factorial(10)).toBe(3628800); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | - As much as possible, try to follow the existing format of markdown and code. 4 | - Don't forget to run `npm run lint` and `npm test` before submitting pull requests. 5 | - Make sure that **100%** of your code is covered by tests. 6 | - If you're adding **new** algorithms or data structures please provide **README.md** for each of them **with explanations** of the algorithm and **with links** to further readings. 7 | -------------------------------------------------------------------------------- /src/algorithms/sets/power-set/README.md: -------------------------------------------------------------------------------- 1 | # Power Set 2 | 3 | Power set of a set A is the set of all of the subsets of A. 4 | 5 | Eg. for `{x, y, z}`, the subsets are : `{{}, {x}, {y}, {z}, {x, y}, {x, z}, {y, z}, {x, y, z}}` 6 | 7 | ![Power Set](https://www.mathsisfun.com/sets/images/power-set.svg) 8 | 9 | ## References 10 | 11 | * [Wikipedia](https://en.wikipedia.org/wiki/Power_set) 12 | * [Math is Fun](https://www.mathsisfun.com/sets/power-set.html) 13 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/unique-paths/uniquePaths.js: -------------------------------------------------------------------------------- 1 | import pascalTriangle from '../../math/pascal-triangle/pascalTriangle'; 2 | 3 | /** 4 | * @param {number} width 5 | * @param {number} height 6 | * @return {number} 7 | */ 8 | export default function uniquePaths(width, height) { 9 | const pascalLine = width + height - 2; 10 | const pascalLinePosition = Math.min(width, height) - 1; 11 | 12 | return pascalTriangle(pascalLine)[pascalLinePosition]; 13 | } 14 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/__test__/divideByTwo.test.js: -------------------------------------------------------------------------------- 1 | import divideByTwo from '../divideByTwo'; 2 | 3 | describe('divideByTwo', () => { 4 | it('should divide numbers by two using bitwise operations', () => { 5 | expect(divideByTwo(0)).toBe(0); 6 | expect(divideByTwo(1)).toBe(0); 7 | expect(divideByTwo(3)).toBe(1); 8 | expect(divideByTwo(10)).toBe(5); 9 | expect(divideByTwo(17)).toBe(8); 10 | expect(divideByTwo(125)).toBe(62); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/algorithms/sets/cartesian-product/cartesianProduct.js: -------------------------------------------------------------------------------- 1 | export default function cartesianProduct(setA, setB) { 2 | if (!setA || !setB || !setA.length || !setB.length) { 3 | return null; 4 | } 5 | 6 | const product = []; 7 | 8 | for (let indexA = 0; indexA < setA.length; indexA += 1) { 9 | for (let indexB = 0; indexB < setB.length; indexB += 1) { 10 | product.push([setA[indexA], setB[indexB]]); 11 | } 12 | } 13 | 14 | return product; 15 | } 16 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/__test__/setBit.test.js: -------------------------------------------------------------------------------- 1 | import setBit from '../setBit'; 2 | 3 | describe('setBit', () => { 4 | it('should set bit at specific position', () => { 5 | // 1 = 0b0001 6 | expect(setBit(1, 0)).toBe(1); 7 | expect(setBit(1, 1)).toBe(3); 8 | expect(setBit(1, 2)).toBe(5); 9 | 10 | // 10 = 0b1010 11 | expect(setBit(10, 0)).toBe(11); 12 | expect(setBit(10, 1)).toBe(10); 13 | expect(setBit(10, 2)).toBe(14); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/algorithms/math/factorial/__test__/factorialRecursive.test.js: -------------------------------------------------------------------------------- 1 | import factorialRecursive from '../factorialRecursive'; 2 | 3 | describe('factorialRecursive', () => { 4 | it('should calculate factorial', () => { 5 | expect(factorialRecursive(0)).toBe(1); 6 | expect(factorialRecursive(1)).toBe(1); 7 | expect(factorialRecursive(5)).toBe(120); 8 | expect(factorialRecursive(8)).toBe(40320); 9 | expect(factorialRecursive(10)).toBe(3628800); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/algorithms/string/hamming-distance/hammingDistance.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} a 3 | * @param {string} b 4 | * @return {number} 5 | */ 6 | export default function hammingDistance(a, b) { 7 | if (a.length !== b.length) { 8 | throw new Error('Strings must be of the same length'); 9 | } 10 | 11 | let distance = 0; 12 | 13 | for (let i = 0; i < a.length; i += 1) { 14 | if (a[i] !== b[i]) { 15 | distance += 1; 16 | } 17 | } 18 | 19 | return distance; 20 | } 21 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/__test__/clearBit.test.js: -------------------------------------------------------------------------------- 1 | import clearBit from '../clearBit'; 2 | 3 | describe('clearBit', () => { 4 | it('should clear bit at specific position', () => { 5 | // 1 = 0b0001 6 | expect(clearBit(1, 0)).toBe(0); 7 | expect(clearBit(1, 1)).toBe(1); 8 | expect(clearBit(1, 2)).toBe(1); 9 | 10 | // 10 = 0b1010 11 | expect(clearBit(10, 0)).toBe(10); 12 | expect(clearBit(10, 1)).toBe(8); 13 | expect(clearBit(10, 3)).toBe(2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/__test__/multiplyByTwo.test.js: -------------------------------------------------------------------------------- 1 | import multiplyByTwo from '../multiplyByTwo'; 2 | 3 | describe('multiplyByTwo', () => { 4 | it('should multiply numbers by two using bitwise operations', () => { 5 | expect(multiplyByTwo(0)).toBe(0); 6 | expect(multiplyByTwo(1)).toBe(2); 7 | expect(multiplyByTwo(3)).toBe(6); 8 | expect(multiplyByTwo(10)).toBe(20); 9 | expect(multiplyByTwo(17)).toBe(34); 10 | expect(multiplyByTwo(125)).toBe(250); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/unique-paths/__test__/uniquePaths.test.js: -------------------------------------------------------------------------------- 1 | import uniquePaths from '../uniquePaths'; 2 | 3 | describe('uniquePaths', () => { 4 | it('should find the number of unique paths on board', () => { 5 | expect(uniquePaths(3, 2)).toBe(3); 6 | expect(uniquePaths(7, 3)).toBe(28); 7 | expect(uniquePaths(3, 7)).toBe(28); 8 | expect(uniquePaths(10, 10)).toBe(48620); 9 | expect(uniquePaths(100, 1)).toBe(1); 10 | expect(uniquePaths(1, 100)).toBe(1); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/__test__/switchSign.test.js: -------------------------------------------------------------------------------- 1 | import switchSign from '../switchSign'; 2 | 3 | describe('switchSign', () => { 4 | it('should switch the sign of the number using twos complement approach', () => { 5 | expect(switchSign(0)).toBe(0); 6 | expect(switchSign(1)).toBe(-1); 7 | expect(switchSign(-1)).toBe(1); 8 | expect(switchSign(32)).toBe(-32); 9 | expect(switchSign(-32)).toBe(32); 10 | expect(switchSign(23)).toBe(-23); 11 | expect(switchSign(-23)).toBe(23); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/unique-paths/__test__/btUniquePaths.test.js: -------------------------------------------------------------------------------- 1 | import btUniquePaths from '../btUniquePaths'; 2 | 3 | describe('btUniquePaths', () => { 4 | it('should find the number of unique paths on board', () => { 5 | expect(btUniquePaths(3, 2)).toBe(3); 6 | expect(btUniquePaths(7, 3)).toBe(28); 7 | expect(btUniquePaths(3, 7)).toBe(28); 8 | expect(btUniquePaths(10, 10)).toBe(48620); 9 | expect(btUniquePaths(100, 1)).toBe(1); 10 | expect(btUniquePaths(1, 100)).toBe(1); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/unique-paths/__test__/dpUniquePaths.test.js: -------------------------------------------------------------------------------- 1 | import dpUniquePaths from '../dpUniquePaths'; 2 | 3 | describe('dpUniquePaths', () => { 4 | it('should find the number of unique paths on board', () => { 5 | expect(dpUniquePaths(3, 2)).toBe(3); 6 | expect(dpUniquePaths(7, 3)).toBe(28); 7 | expect(dpUniquePaths(3, 7)).toBe(28); 8 | expect(dpUniquePaths(10, 10)).toBe(48620); 9 | expect(dpUniquePaths(100, 1)).toBe(1); 10 | expect(dpUniquePaths(1, 100)).toBe(1); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/algorithms/sets/fisher-yates/fisherYates.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {*[]} originalArray 3 | * @return {*[]} 4 | */ 5 | export default function fisherYates(originalArray) { 6 | // Clone array from preventing original array from modification (for testing purpose). 7 | const array = originalArray.slice(0); 8 | 9 | for (let i = (array.length - 1); i > 0; i -= 1) { 10 | const randomIndex = Math.floor(Math.random() * (i + 1)); 11 | [array[i], array[randomIndex]] = [array[randomIndex], array[i]]; 12 | } 13 | 14 | return array; 15 | } 16 | -------------------------------------------------------------------------------- /src/algorithms/math/pascal-triangle/pascalTriangle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} lineNumber - zero based. 3 | * @return {number[]} 4 | */ 5 | export default function pascalTriangle(lineNumber) { 6 | const currentLine = [1]; 7 | 8 | const currentLineSize = lineNumber + 1; 9 | 10 | for (let numIndex = 1; numIndex < currentLineSize; numIndex += 1) { 11 | // See explanation of this formula in README. 12 | currentLine[numIndex] = currentLine[numIndex - 1] * (lineNumber - numIndex + 1) / numIndex; 13 | } 14 | 15 | return currentLine; 16 | } 17 | -------------------------------------------------------------------------------- /src/algorithms/math/sieve-of-eratosthenes/__test__/sieveOfEratosthenes.test.js: -------------------------------------------------------------------------------- 1 | import sieveOfEratosthenes from '../sieveOfEratosthenes'; 2 | 3 | describe('sieveOfEratosthenes', () => { 4 | it('should find all primes less than or equal to n', () => { 5 | expect(sieveOfEratosthenes(5)).toEqual([2, 3, 5]); 6 | expect(sieveOfEratosthenes(10)).toEqual([2, 3, 5, 7]); 7 | expect(sieveOfEratosthenes(100)).toEqual([ 8 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 9 | 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 10 | ]); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/updateBit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @param {number} bitPosition - zero based. 4 | * @param {number} bitValue - 0 or 1. 5 | * @return {number} 6 | */ 7 | export default function updateBit(number, bitPosition, bitValue) { 8 | // Normalized bit value. 9 | const bitValueNormalized = bitValue ? 1 : 0; 10 | 11 | // Init clear mask. 12 | const clearMask = ~(1 << bitPosition); 13 | 14 | // Clear bit value and then set it up to required value. 15 | return (number & clearMask) | (bitValueNormalized << bitPosition); 16 | } 17 | -------------------------------------------------------------------------------- /src/algorithms/sets/power-set/__test__/powerSet.test.js: -------------------------------------------------------------------------------- 1 | import powerSet from '../powerSet'; 2 | 3 | describe('powerSet', () => { 4 | it('should calculate power set of given set', () => { 5 | const powerSets1 = powerSet([1]); 6 | const powerSets2 = powerSet([1, 2, 3]); 7 | 8 | expect(powerSets1).toEqual([ 9 | [], 10 | [1], 11 | ]); 12 | 13 | expect(powerSets2).toEqual([ 14 | [], 15 | [1], 16 | [2], 17 | [1, 2], 18 | [3], 19 | [1, 3], 20 | [2, 3], 21 | [1, 2, 3], 22 | ]); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/algorithms/math/fibonacci/__test__/fibonacciNth.test.js: -------------------------------------------------------------------------------- 1 | import fibonacciNth from '../fibonacciNth'; 2 | 3 | describe('fibonacciNth', () => { 4 | it('should calculate fibonacci correctly', () => { 5 | expect(fibonacciNth(1)).toBe(1); 6 | expect(fibonacciNth(2)).toBe(1); 7 | expect(fibonacciNth(3)).toBe(2); 8 | expect(fibonacciNth(4)).toBe(3); 9 | expect(fibonacciNth(5)).toBe(5); 10 | expect(fibonacciNth(6)).toBe(8); 11 | expect(fibonacciNth(7)).toBe(13); 12 | expect(fibonacciNth(8)).toBe(21); 13 | expect(fibonacciNth(20)).toBe(6765); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/algorithms/math/integer-partition/__test__/integerPartition.test.js: -------------------------------------------------------------------------------- 1 | import integerPartition from '../integerPartition'; 2 | 3 | describe('integerPartition', () => { 4 | it('should partition the number', () => { 5 | expect(integerPartition(1)).toBe(1); 6 | expect(integerPartition(2)).toBe(2); 7 | expect(integerPartition(3)).toBe(3); 8 | expect(integerPartition(4)).toBe(5); 9 | expect(integerPartition(5)).toBe(7); 10 | expect(integerPartition(6)).toBe(11); 11 | expect(integerPartition(7)).toBe(15); 12 | expect(integerPartition(8)).toBe(22); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/algorithms/math/liu-hui/__test__/liuHui.test.js: -------------------------------------------------------------------------------- 1 | import liuHui from '../liuHui'; 2 | 3 | describe('liuHui', () => { 4 | it('should calculate π based on 12-gon', () => { 5 | expect(liuHui(1)).toBe(3); 6 | }); 7 | 8 | it('should calculate π based on 24-gon', () => { 9 | expect(liuHui(2)).toBe(3.105828541230249); 10 | }); 11 | 12 | it('should calculate π based on 6144-gon', () => { 13 | expect(liuHui(10)).toBe(3.1415921059992717); 14 | }); 15 | 16 | it('should calculate π based on 201326592-gon', () => { 17 | expect(liuHui(25)).toBe(3.141592653589793); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/algorithms/string/longest-common-substring/__test__/longestCommonSubstring.test.js: -------------------------------------------------------------------------------- 1 | import longestCommonSubstring from '../longestCommonSubstring'; 2 | 3 | describe('longestCommonSubstring', () => { 4 | it('should find longest common substring between two strings', () => { 5 | expect(longestCommonSubstring('', '')).toBe(''); 6 | expect(longestCommonSubstring('ABC', '')).toBe(''); 7 | expect(longestCommonSubstring('', 'ABC')).toBe(''); 8 | expect(longestCommonSubstring('ABABC', 'BABCA')).toBe('BABC'); 9 | expect(longestCommonSubstring('BABCA', 'ABCBA')).toBe('ABC'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/algorithms/math/fibonacci/fibonacciNth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Calculate fibonacci number at specific position using Dynamic Programming approach. 3 | * 4 | * @param n 5 | * @return {number} 6 | */ 7 | export default function fibonacciNth(n) { 8 | let currentValue = 1; 9 | let previousValue = 0; 10 | 11 | if (n === 1) { 12 | return 1; 13 | } 14 | 15 | let iterationsCounter = n - 1; 16 | 17 | while (iterationsCounter) { 18 | currentValue += previousValue; 19 | previousValue = currentValue - previousValue; 20 | 21 | iterationsCounter -= 1; 22 | } 23 | 24 | return currentValue; 25 | } 26 | -------------------------------------------------------------------------------- /src/algorithms/sets/cartesian-product/README.md: -------------------------------------------------------------------------------- 1 | # Cartesian Product 2 | 3 | In set theory a Cartesian product is a mathematical operation that returns a set 4 | (or product set or simply product) from multiple sets. That is, for sets A and B, 5 | the Cartesian product A × B is the set of all ordered pairs (a, b) 6 | where a ∈ A and b ∈ B. 7 | 8 | Cartesian product `AxB` of two sets `A={x,y,z}` and `B={1,2,3}` 9 | 10 | ![Cartesian Product of Two Sets](https://upload.wikimedia.org/wikipedia/commons/4/4e/Cartesian_Product_qtl1.svg) 11 | 12 | ## References 13 | 14 | [Wikipedia](https://en.wikipedia.org/wiki/Cartesian_product) 15 | -------------------------------------------------------------------------------- /src/algorithms/math/fibonacci/__test__/fibonacci.test.js: -------------------------------------------------------------------------------- 1 | import fibonacci from '../fibonacci'; 2 | 3 | describe('fibonacci', () => { 4 | it('should calculate fibonacci correctly', () => { 5 | expect(fibonacci(1)).toEqual([1]); 6 | expect(fibonacci(2)).toEqual([1, 1]); 7 | expect(fibonacci(3)).toEqual([1, 1, 2]); 8 | expect(fibonacci(4)).toEqual([1, 1, 2, 3]); 9 | expect(fibonacci(5)).toEqual([1, 1, 2, 3, 5]); 10 | expect(fibonacci(6)).toEqual([1, 1, 2, 3, 5, 8]); 11 | expect(fibonacci(7)).toEqual([1, 1, 2, 3, 5, 8, 13]); 12 | expect(fibonacci(8)).toEqual([1, 1, 2, 3, 5, 8, 13, 21]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/algorithms/math/fibonacci/fibonacci.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Return a fibonacci sequence as an array. 3 | * 4 | * @param n 5 | * @return {number[]} 6 | */ 7 | export default function fibonacci(n) { 8 | const fibSequence = [1]; 9 | 10 | let currentValue = 1; 11 | let previousValue = 0; 12 | 13 | if (n === 1) { 14 | return fibSequence; 15 | } 16 | 17 | let iterationsCounter = n - 1; 18 | 19 | while (iterationsCounter) { 20 | currentValue += previousValue; 21 | previousValue = currentValue - previousValue; 22 | 23 | fibSequence.push(currentValue); 24 | 25 | iterationsCounter -= 1; 26 | } 27 | 28 | return fibSequence; 29 | } 30 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/n-queens/__test__/QueensPosition.test.js: -------------------------------------------------------------------------------- 1 | import QueenPosition from '../QueenPosition'; 2 | 3 | describe('QueenPosition', () => { 4 | it('should store queen position on chessboard', () => { 5 | const position1 = new QueenPosition(0, 0); 6 | const position2 = new QueenPosition(2, 1); 7 | 8 | expect(position2.columnIndex).toBe(1); 9 | expect(position2.rowIndex).toBe(2); 10 | expect(position1.leftDiagonal).toBe(0); 11 | expect(position1.rightDiagonal).toBe(0); 12 | expect(position2.leftDiagonal).toBe(1); 13 | expect(position2.rightDiagonal).toBe(3); 14 | expect(position2.toString()).toBe('2,1'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/__test__/getBit.test.js: -------------------------------------------------------------------------------- 1 | import getBit from '../getBit'; 2 | 3 | describe('getBit', () => { 4 | it('should get bit at specific position', () => { 5 | // 1 = 0b0001 6 | expect(getBit(1, 0)).toBe(1); 7 | expect(getBit(1, 1)).toBe(0); 8 | 9 | // 2 = 0b0010 10 | expect(getBit(2, 0)).toBe(0); 11 | expect(getBit(2, 1)).toBe(1); 12 | 13 | // 3 = 0b0011 14 | expect(getBit(3, 0)).toBe(1); 15 | expect(getBit(3, 1)).toBe(1); 16 | 17 | // 10 = 0b1010 18 | expect(getBit(10, 0)).toBe(0); 19 | expect(getBit(10, 1)).toBe(1); 20 | expect(getBit(10, 2)).toBe(0); 21 | expect(getBit(10, 3)).toBe(1); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/algorithms/search/linear-search/linearSearch.js: -------------------------------------------------------------------------------- 1 | import Comparator from '../../../utils/comparator/Comparator'; 2 | 3 | /** 4 | * Linear search implementation. 5 | * 6 | * @param {*[]} array 7 | * @param {*} seekElement 8 | * @param {function(a, b)} [comparatorCallback] 9 | * @return {number[]} 10 | */ 11 | export default function linearSearch(array, seekElement, comparatorCallback) { 12 | const comparator = new Comparator(comparatorCallback); 13 | const foundIndices = []; 14 | 15 | array.forEach((element, index) => { 16 | if (comparator.equal(element, seekElement)) { 17 | foundIndices.push(index); 18 | } 19 | }); 20 | 21 | return foundIndices; 22 | } 23 | -------------------------------------------------------------------------------- /src/algorithms/sets/combination-sum/__test__/combinationSum.test.js: -------------------------------------------------------------------------------- 1 | import combinationSum from '../combinationSum'; 2 | 3 | describe('combinationSum', () => { 4 | it('should find all combinations with specific sum', () => { 5 | expect(combinationSum([1], 4)).toEqual([ 6 | [1, 1, 1, 1], 7 | ]); 8 | 9 | expect(combinationSum([2, 3, 6, 7], 7)).toEqual([ 10 | [2, 2, 3], 11 | [7], 12 | ]); 13 | 14 | expect(combinationSum([2, 3, 5], 8)).toEqual([ 15 | [2, 2, 2, 2], 16 | [2, 3, 3], 17 | [3, 5], 18 | ]); 19 | 20 | expect(combinationSum([2, 5], 3)).toEqual([]); 21 | 22 | expect(combinationSum([], 3)).toEqual([]); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/algorithms/math/bits/__test__/updateBit.test.js: -------------------------------------------------------------------------------- 1 | import updateBit from '../updateBit'; 2 | 3 | describe('updateBit', () => { 4 | it('should update bit at specific position', () => { 5 | // 1 = 0b0001 6 | expect(updateBit(1, 0, 1)).toBe(1); 7 | expect(updateBit(1, 0, 0)).toBe(0); 8 | expect(updateBit(1, 1, 1)).toBe(3); 9 | expect(updateBit(1, 2, 1)).toBe(5); 10 | 11 | // 10 = 0b1010 12 | expect(updateBit(10, 0, 1)).toBe(11); 13 | expect(updateBit(10, 0, 0)).toBe(10); 14 | expect(updateBit(10, 1, 1)).toBe(10); 15 | expect(updateBit(10, 1, 0)).toBe(8); 16 | expect(updateBit(10, 2, 1)).toBe(14); 17 | expect(updateBit(10, 2, 0)).toBe(10); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/algorithms/math/is-power-of-two/isPowerOfTwo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @return {boolean} 4 | */ 5 | export default function isPowerOfTwo(number) { 6 | // 1 (2^0) is the smallest power of two. 7 | if (number < 1) { 8 | return false; 9 | } 10 | 11 | // Let's find out if we can divide the number by two 12 | // many times without remainder. 13 | let dividedNumber = number; 14 | while (dividedNumber !== 1) { 15 | if (dividedNumber % 2 !== 0) { 16 | // For every case when remainder isn't zero we can say that this number 17 | // couldn't be a result of power of two. 18 | return false; 19 | } 20 | 21 | dividedNumber /= 2; 22 | } 23 | 24 | return true; 25 | } 26 | -------------------------------------------------------------------------------- /src/algorithms/math/pascal-triangle/__test__/pascalTriangle.test.js: -------------------------------------------------------------------------------- 1 | import pascalTriangle from '../pascalTriangle'; 2 | 3 | describe('pascalTriangle', () => { 4 | it('should calculate Pascal Triangle coefficients for specific line number', () => { 5 | expect(pascalTriangle(0)).toEqual([1]); 6 | expect(pascalTriangle(1)).toEqual([1, 1]); 7 | expect(pascalTriangle(2)).toEqual([1, 2, 1]); 8 | expect(pascalTriangle(3)).toEqual([1, 3, 3, 1]); 9 | expect(pascalTriangle(4)).toEqual([1, 4, 6, 4, 1]); 10 | expect(pascalTriangle(5)).toEqual([1, 5, 10, 10, 5, 1]); 11 | expect(pascalTriangle(6)).toEqual([1, 6, 15, 20, 15, 6, 1]); 12 | expect(pascalTriangle(7)).toEqual([1, 7, 21, 35, 35, 21, 7, 1]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/data-structures/queue/Queue.js: -------------------------------------------------------------------------------- 1 | import LinkedList from '../linked-list/LinkedList'; 2 | 3 | export default class Queue { 4 | constructor() { 5 | this.linkedList = new LinkedList(); 6 | } 7 | 8 | isEmpty() { 9 | return !this.linkedList.tail; 10 | } 11 | 12 | peek() { 13 | if (!this.linkedList.head) { 14 | return null; 15 | } 16 | 17 | return this.linkedList.head.value; 18 | } 19 | 20 | enqueue(value) { 21 | this.linkedList.append(value); 22 | } 23 | 24 | dequeue() { 25 | const removedHead = this.linkedList.deleteHead(); 26 | return removedHead ? removedHead.value : null; 27 | } 28 | 29 | toString(callback) { 30 | return this.linkedList.toString(callback); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/algorithms/string/longest-common-substring/README.md: -------------------------------------------------------------------------------- 1 | # Longest Common Substring Problem 2 | 3 | The longest common substring problem is to find the longest string 4 | (or strings) that is a substring (or are substrings) of two or more 5 | strings. 6 | 7 | ## Example 8 | 9 | The longest common substring of the strings `ABABC`, `BABCA` and 10 | `ABCBA` is string `ABC` of length 3. Other common substrings are 11 | `A`, `AB`, `B`, `BA`, `BC` and `C`. 12 | 13 | ``` 14 | ABABC 15 | ||| 16 | BABCA 17 | ||| 18 | ABCBA 19 | ``` 20 | 21 | ## References 22 | 23 | - [Wikipedia](https://en.wikipedia.org/wiki/Longest_common_substring_problem) 24 | - [YouTube](https://www.youtube.com/watch?v=BysNXJHzCEs&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 25 | -------------------------------------------------------------------------------- /src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js: -------------------------------------------------------------------------------- 1 | import bfMaximumSubarray from '../bfMaximumSubarray'; 2 | 3 | describe('bfMaximumSubarray', () => { 4 | it('should find maximum subarray using brute force algorithm', () => { 5 | expect(bfMaximumSubarray([])).toEqual([]); 6 | expect(bfMaximumSubarray([-1, -2, -3, -4, -5])).toEqual([-1]); 7 | expect(bfMaximumSubarray([1, 2, 3, 2, 3, 4, 5])).toEqual([1, 2, 3, 2, 3, 4, 5]); 8 | expect(bfMaximumSubarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toEqual([4, -1, 2, 1]); 9 | expect(bfMaximumSubarray([-2, -3, 4, -1, -2, 1, 5, -3])).toEqual([4, -1, -2, 1, 5]); 10 | expect(bfMaximumSubarray([1, -3, 2, -5, 7, 6, -1, 4, 11, -23])).toEqual([7, 6, -1, 4, 11]); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/algorithms/sets/cartesian-product/__test__/cartesianProduct.test.js: -------------------------------------------------------------------------------- 1 | import cartesianProduct from '../cartesianProduct'; 2 | 3 | describe('cartesianProduct', () => { 4 | it('should return null if there is not enough info for calculation', () => { 5 | const product1 = cartesianProduct([1], null); 6 | const product2 = cartesianProduct([], null); 7 | 8 | expect(product1).toBeNull(); 9 | expect(product2).toBeNull(); 10 | }); 11 | 12 | it('should calculate the product of two sets', () => { 13 | const product1 = cartesianProduct([1], [1]); 14 | const product2 = cartesianProduct([1, 2], [3, 5]); 15 | 16 | expect(product1).toEqual([[1, 1]]); 17 | expect(product2).toEqual([[1, 3], [1, 5], [2, 3], [2, 5]]); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js: -------------------------------------------------------------------------------- 1 | import dpMaximumSubarray from '../dpMaximumSubarray'; 2 | 3 | describe('dpMaximumSubarray', () => { 4 | it('should find maximum subarray using dynamic programming algorithm', () => { 5 | expect(dpMaximumSubarray([])).toEqual([]); 6 | expect(dpMaximumSubarray([-1, -2, -3, -4, -5])).toEqual([-1]); 7 | expect(dpMaximumSubarray([1, 2, 3, 2, 3, 4, 5])).toEqual([1, 2, 3, 2, 3, 4, 5]); 8 | expect(dpMaximumSubarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toEqual([4, -1, 2, 1]); 9 | expect(dpMaximumSubarray([-2, -3, 4, -1, -2, 1, 5, -3])).toEqual([4, -1, -2, 1, 5]); 10 | expect(dpMaximumSubarray([1, -3, 2, -5, 7, 6, -1, 4, 11, -23])).toEqual([7, 6, -1, 4, 11]); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/algorithms/sets/fisher-yates/README.md: -------------------------------------------------------------------------------- 1 | # Fisher–Yates shuffle 2 | 3 | The Fisher–Yates shuffle is an algorithm for generating a random 4 | permutation of a finite sequence—in plain terms, the algorithm 5 | shuffles the sequence. The algorithm effectively puts all the 6 | elements into a hat; it continually determines the next element 7 | by randomly drawing an element from the hat until no elements 8 | remain. The algorithm produces an unbiased permutation: every 9 | permutation is equally likely. The modern version of the 10 | algorithm is efficient: it takes time proportional to the 11 | number of items being shuffled and shuffles them in place. 12 | 13 | ## References 14 | 15 | [Wikipedia](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) 16 | -------------------------------------------------------------------------------- /src/algorithms/sets/fisher-yates/__test__/fisherYates.test.js: -------------------------------------------------------------------------------- 1 | import fisherYates from '../fisherYates'; 2 | import { sortedArr } from '../../../sorting/SortTester'; 3 | import QuickSort from '../../../sorting/quick-sort/QuickSort'; 4 | 5 | describe('fisherYates', () => { 6 | it('should shuffle small arrays', () => { 7 | expect(fisherYates([])).toEqual([]); 8 | expect(fisherYates([1])).toEqual([1]); 9 | }); 10 | 11 | it('should shuffle array randomly', () => { 12 | const shuffledArray = fisherYates(sortedArr); 13 | const sorter = new QuickSort(); 14 | 15 | expect(shuffledArray.length).toBe(sortedArr.length); 16 | expect(shuffledArray).not.toEqual(sortedArr); 17 | expect(sorter.sort(shuffledArray)).toEqual(sortedArr); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/algorithms/sets/shortest-common-supersequence/README.md: -------------------------------------------------------------------------------- 1 | # Shortest Common Supersequence 2 | 3 | The shortest common supersequence (SCS) of two sequences `X` and `Y` 4 | is the shortest sequence which has `X` and `Y` as subsequences. 5 | 6 | In other words assume we're given two strings str1 and str2, find 7 | the shortest string that has both str1 and str2 as subsequences. 8 | 9 | This is a problem closely related to the longest common 10 | subsequence problem. 11 | 12 | ## Example 13 | 14 | ``` 15 | Input: str1 = "geek", str2 = "eke" 16 | Output: "geeke" 17 | 18 | Input: str1 = "AGGTAB", str2 = "GXTXAYB" 19 | Output: "AGXGTXAYB" 20 | ``` 21 | 22 | ## References 23 | 24 | - [GeeksForGeeks](https://www.geeksforgeeks.org/shortest-common-supersequence/) 25 | -------------------------------------------------------------------------------- /src/algorithms/graph/depth-first-search/README.md: -------------------------------------------------------------------------------- 1 | # Depth-First Search (DFS) 2 | 3 | Depth-first search (DFS) is an algorithm for traversing or 4 | searching tree or graph data structures. One starts at 5 | the root (selecting some arbitrary node as the root in 6 | the case of a graph) and explores as far as possible 7 | along each branch before backtracking. 8 | 9 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/7/7f/Depth-First-Search.gif) 10 | 11 | ## References 12 | 13 | - [Wikipedia](https://en.wikipedia.org/wiki/Depth-first_search) 14 | - [Tree Traversals (Inorder, Preorder and Postorder)](https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/) 15 | - [BFS vs DFS](https://www.geeksforgeeks.org/bfs-vs-dfs-binary-tree/) 16 | -------------------------------------------------------------------------------- /src/algorithms/tree/depth-first-search/README.md: -------------------------------------------------------------------------------- 1 | # Depth-First Search (DFS) 2 | 3 | Depth-first search (DFS) is an algorithm for traversing or 4 | searching tree or graph data structures. One starts at 5 | the root (selecting some arbitrary node as the root in 6 | the case of a graph) and explores as far as possible 7 | along each branch before backtracking. 8 | 9 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/7/7f/Depth-First-Search.gif) 10 | 11 | ## References 12 | 13 | - [Wikipedia](https://en.wikipedia.org/wiki/Depth-first_search) 14 | - [Tree Traversals (Inorder, Preorder and Postorder)](https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/) 15 | - [BFS vs DFS](https://www.geeksforgeeks.org/bfs-vs-dfs-binary-tree/) 16 | -------------------------------------------------------------------------------- /src/algorithms/graph/strongly-connected-components/README.md: -------------------------------------------------------------------------------- 1 | # Strongly Connected Component 2 | 3 | A directed graph is called **strongly connected** if there is a path 4 | in each direction between each pair of vertices of the graph. 5 | In a directed graph G that may not itself be strongly connected, 6 | a pair of vertices `u` and `v` are said to be strongly connected 7 | to each other if there is a path in each direction between them. 8 | 9 | ![Strongly Connected](https://upload.wikimedia.org/wikipedia/commons/5/5c/Scc.png) 10 | 11 | Graph with strongly connected components marked 12 | 13 | ## References 14 | 15 | - [Wikipedia](https://en.wikipedia.org/wiki/Strongly_connected_component) 16 | - [YouTube](https://www.youtube.com/watch?v=RpgcYiky7uw&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 17 | -------------------------------------------------------------------------------- /src/algorithms/graph/breadth-first-search/README.md: -------------------------------------------------------------------------------- 1 | # Breadth-First Search (BFS) 2 | 3 | Breadth-first search (BFS) is an algorithm for traversing 4 | or searching tree or graph data structures. It starts at 5 | the tree root (or some arbitrary node of a graph, sometimes 6 | referred to as a 'search key') and explores the neighbor 7 | nodes first, before moving to the next level neighbors. 8 | 9 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/5/5d/Breadth-First-Search-Algorithm.gif) 10 | 11 | ## References 12 | 13 | - [Wikipedia](https://en.wikipedia.org/wiki/Breadth-first_search) 14 | - [Tree Traversals (Inorder, Preorder and Postorder)](https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/) 15 | - [BFS vs DFS](https://www.geeksforgeeks.org/bfs-vs-dfs-binary-tree/) 16 | -------------------------------------------------------------------------------- /src/algorithms/tree/breadth-first-search/README.md: -------------------------------------------------------------------------------- 1 | # Breadth-First Search (BFS) 2 | 3 | Breadth-first search (BFS) is an algorithm for traversing 4 | or searching tree or graph data structures. It starts at 5 | the tree root (or some arbitrary node of a graph, sometimes 6 | referred to as a 'search key') and explores the neighbor 7 | nodes first, before moving to the next level neighbors. 8 | 9 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/5/5d/Breadth-First-Search-Algorithm.gif) 10 | 11 | ## References 12 | 13 | - [Wikipedia](https://en.wikipedia.org/wiki/Breadth-first_search) 14 | - [Tree Traversals (Inorder, Preorder and Postorder)](https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/) 15 | - [BFS vs DFS](https://www.geeksforgeeks.org/bfs-vs-dfs-binary-tree/) 16 | -------------------------------------------------------------------------------- /src/algorithms/string/knuth-morris-pratt/README.md: -------------------------------------------------------------------------------- 1 | # Knuth–Morris–Pratt Algorithm 2 | 3 | The Knuth–Morris–Pratt string searching algorithm (or 4 | KMP algorithm) searches for occurrences of a "word" `W` 5 | within a main "text string" `T` by employing the 6 | observation that when a mismatch occurs, the word itself 7 | embodies sufficient information to determine where the 8 | next match could begin, thus bypassing re-examination 9 | of previously matched characters. 10 | 11 | ## Complexity 12 | 13 | - **Time:** `O(|W| + |T|)` (much faster comparing to trivial `O(|W| * |T|)`) 14 | - **Space:** `O(|W|)` 15 | 16 | ## References 17 | 18 | - [Wikipedia](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) 19 | - [YouTube](https://www.youtube.com/watch?v=GTJr8OvyEVQ&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 20 | -------------------------------------------------------------------------------- /src/algorithms/math/is-power-of-two/isPowerOfTwoBitwise.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @return {boolean} 4 | */ 5 | export default function isPowerOfTwoBitwise(number) { 6 | // 1 (2^0) is the smallest power of two. 7 | if (number < 1) { 8 | return false; 9 | } 10 | 11 | /* 12 | * Powers of two in binary look like this: 13 | * 1: 0001 14 | * 2: 0010 15 | * 4: 0100 16 | * 8: 1000 17 | * 18 | * Note that there is always exactly 1 bit set. The only exception is with a signed integer. 19 | * e.g. An 8-bit signed integer with a value of -128 looks like: 20 | * 10000000 21 | * 22 | * So after checking that the number is greater than zero, we can use a clever little bit 23 | * hack to test that one and only one bit is set. 24 | */ 25 | return (number & (number - 1)) === 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/algorithms/math/pascal-triangle/__test__/pascalTriangleRecursive.test.js: -------------------------------------------------------------------------------- 1 | import pascalTriangleRecursive from '../pascalTriangleRecursive'; 2 | 3 | describe('pascalTriangleRecursive', () => { 4 | it('should calculate Pascal Triangle coefficients for specific line number', () => { 5 | expect(pascalTriangleRecursive(0)).toEqual([1]); 6 | expect(pascalTriangleRecursive(1)).toEqual([1, 1]); 7 | expect(pascalTriangleRecursive(2)).toEqual([1, 2, 1]); 8 | expect(pascalTriangleRecursive(3)).toEqual([1, 3, 3, 1]); 9 | expect(pascalTriangleRecursive(4)).toEqual([1, 4, 6, 4, 1]); 10 | expect(pascalTriangleRecursive(5)).toEqual([1, 5, 10, 10, 5, 1]); 11 | expect(pascalTriangleRecursive(6)).toEqual([1, 6, 15, 20, 15, 6, 1]); 12 | expect(pascalTriangleRecursive(7)).toEqual([1, 7, 21, 35, 35, 21, 7, 1]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/jump-game/__test__/greedyJumpGame.test.js: -------------------------------------------------------------------------------- 1 | import greedyJumpGame from '../greedyJumpGame'; 2 | 3 | describe('greedyJumpGame', () => { 4 | it('should solve Jump Game problem in greedy manner', () => { 5 | expect(greedyJumpGame([1, 0])).toBeTruthy(); 6 | expect(greedyJumpGame([100, 0])).toBeTruthy(); 7 | expect(greedyJumpGame([2, 3, 1, 1, 4])).toBeTruthy(); 8 | expect(greedyJumpGame([1, 1, 1, 1, 1])).toBeTruthy(); 9 | expect(greedyJumpGame([1, 1, 1, 10, 1])).toBeTruthy(); 10 | expect(greedyJumpGame([1, 5, 2, 1, 0, 2, 0])).toBeTruthy(); 11 | 12 | expect(greedyJumpGame([1, 0, 1])).toBeFalsy(); 13 | expect(greedyJumpGame([3, 2, 1, 0, 4])).toBeFalsy(); 14 | expect(greedyJumpGame([0, 0, 0, 0, 0])).toBeFalsy(); 15 | expect(greedyJumpGame([5, 4, 3, 2, 1, 0, 0])).toBeFalsy(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/algorithms/string/knuth-morris-pratt/__test__/knuthMorrisPratt.test.js: -------------------------------------------------------------------------------- 1 | import knuthMorrisPratt from '../knuthMorrisPratt'; 2 | 3 | describe('knuthMorrisPratt', () => { 4 | it('should find word position in given text', () => { 5 | expect(knuthMorrisPratt('', '')).toBe(0); 6 | expect(knuthMorrisPratt('a', '')).toBe(0); 7 | expect(knuthMorrisPratt('a', 'a')).toBe(0); 8 | expect(knuthMorrisPratt('abcbcglx', 'abca')).toBe(-1); 9 | expect(knuthMorrisPratt('abcbcglx', 'bcgl')).toBe(3); 10 | expect(knuthMorrisPratt('abcxabcdabxabcdabcdabcy', 'abcdabcy')).toBe(15); 11 | expect(knuthMorrisPratt('abcxabcdabxabcdabcdabcy', 'abcdabca')).toBe(-1); 12 | expect(knuthMorrisPratt('abcxabcdabxaabcdabcabcdabcdabcy', 'abcdabca')).toBe(12); 13 | expect(knuthMorrisPratt('abcxabcdabxaabaabaaaabcdabcdabcy', 'aabaabaaa')).toBe(11); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/algorithms/sets/combinations/combineWithRepetitions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {*[]} comboOptions 3 | * @param {number} comboLength 4 | * @return {*[]} 5 | */ 6 | export default function combineWithRepetitions(comboOptions, comboLength) { 7 | if (comboLength === 1) { 8 | return comboOptions.map(comboOption => [comboOption]); 9 | } 10 | 11 | // Init combinations array. 12 | const combos = []; 13 | 14 | // Eliminate characters one by one and concatenate them to 15 | // combinations of smaller lengths. 16 | comboOptions.forEach((currentOption, optionIndex) => { 17 | const smallerCombos = combineWithRepetitions( 18 | comboOptions.slice(optionIndex), 19 | comboLength - 1, 20 | ); 21 | 22 | smallerCombos.forEach((smallerCombo) => { 23 | combos.push([currentOption].concat(smallerCombo)); 24 | }); 25 | }); 26 | 27 | return combos; 28 | } 29 | -------------------------------------------------------------------------------- /src/algorithms/math/least-common-multiple/__test__/leastCommonMultiple.test.js: -------------------------------------------------------------------------------- 1 | import leastCommonMultiple from '../leastCommonMultiple'; 2 | 3 | describe('leastCommonMultiple', () => { 4 | it('should find least common multiple', () => { 5 | expect(leastCommonMultiple(0, 0)).toBe(0); 6 | expect(leastCommonMultiple(1, 0)).toBe(0); 7 | expect(leastCommonMultiple(0, 1)).toBe(0); 8 | expect(leastCommonMultiple(4, 6)).toBe(12); 9 | expect(leastCommonMultiple(6, 21)).toBe(42); 10 | expect(leastCommonMultiple(7, 2)).toBe(14); 11 | expect(leastCommonMultiple(3, 5)).toBe(15); 12 | expect(leastCommonMultiple(7, 3)).toBe(21); 13 | expect(leastCommonMultiple(1000000, 2)).toBe(1000000); 14 | expect(leastCommonMultiple(-9, -18)).toBe(18); 15 | expect(leastCommonMultiple(-7, -9)).toBe(63); 16 | expect(leastCommonMultiple(-7, 9)).toBe(63); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/data-structures/heap/README.md: -------------------------------------------------------------------------------- 1 | # Heap (data-structure) 2 | 3 | In computer science, a heap is a specialized tree-based 4 | data structure that satisfies the heap property described 5 | below. 6 | 7 | In a *min heap*, if `P` is a parent node of `C`, then the 8 | key (the value) of `P` is less than or equal to the 9 | key of `C`. 10 | 11 | ![MinHeap](https://upload.wikimedia.org/wikipedia/commons/6/69/Min-heap.png) 12 | 13 | In a *max heap*, the key of `P` is greater than or equal 14 | to the key of `C` 15 | 16 | ![Heap](https://upload.wikimedia.org/wikipedia/commons/3/38/Max-Heap.svg) 17 | 18 | The node at the "top" of the heap with no parents is 19 | called the root node. 20 | 21 | ## References 22 | 23 | - [Wikipedia](https://en.wikipedia.org/wiki/Heap_(data_structure)) 24 | - [YouTube](https://www.youtube.com/watch?v=t0Cq6tVNRBA&index=5&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 25 | -------------------------------------------------------------------------------- /src/algorithms/sets/combinations/combineWithoutRepetitions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {*[]} comboOptions 3 | * @param {number} comboLength 4 | * @return {*[]} 5 | */ 6 | export default function combineWithoutRepetitions(comboOptions, comboLength) { 7 | if (comboLength === 1) { 8 | return comboOptions.map(comboOption => [comboOption]); 9 | } 10 | 11 | // Init combinations array. 12 | const combos = []; 13 | 14 | // Eliminate characters one by one and concatenate them to 15 | // combinations of smaller lengths. 16 | comboOptions.forEach((currentOption, optionIndex) => { 17 | const smallerCombos = combineWithoutRepetitions( 18 | comboOptions.slice(optionIndex + 1), 19 | comboLength - 1, 20 | ); 21 | 22 | smallerCombos.forEach((smallerCombo) => { 23 | combos.push([currentOption].concat(smallerCombo)); 24 | }); 25 | }); 26 | 27 | return combos; 28 | } 29 | -------------------------------------------------------------------------------- /src/algorithms/graph/bellman-ford/README.md: -------------------------------------------------------------------------------- 1 | # Bellman–Ford Algorithm 2 | 3 | The Bellman–Ford algorithm is an algorithm that computes shortest 4 | paths from a single source vertex to all of the other vertices 5 | in a weighted digraph. It is slower than Dijkstra's algorithm 6 | for the same problem, but more versatile, as it is capable of 7 | handling graphs in which some of the edge weights are negative 8 | numbers. 9 | 10 | ![Bellman-Ford](https://upload.wikimedia.org/wikipedia/commons/2/2e/Shortest_path_Dijkstra_vs_BellmanFord.gif) 11 | 12 | ## Complexity 13 | 14 | Worst-case performance `O(|V||E|)` 15 | Best-case performance `O(|E|)` 16 | Worst-case space complexity `O(|V|)` 17 | 18 | ## References 19 | 20 | - [Wikipedia](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm) 21 | - [On YouTube by Michael Sambol](https://www.youtube.com/watch?v=obWXjtg0L64&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 22 | -------------------------------------------------------------------------------- /src/algorithms/string/z-algorithm/__test__/zAlgorithm.test.js: -------------------------------------------------------------------------------- 1 | import zAlgorithm from '../zAlgorithm'; 2 | 3 | describe('zAlgorithm', () => { 4 | it('should find word positions in given text', () => { 5 | expect(zAlgorithm('abcbcglx', 'abca')).toEqual([]); 6 | expect(zAlgorithm('abca', 'abca')).toEqual([0]); 7 | expect(zAlgorithm('abca', 'abcadfd')).toEqual([]); 8 | expect(zAlgorithm('abcbcglabcx', 'abc')).toEqual([0, 7]); 9 | expect(zAlgorithm('abcbcglx', 'bcgl')).toEqual([3]); 10 | expect(zAlgorithm('abcbcglx', 'cglx')).toEqual([4]); 11 | expect(zAlgorithm('abcxabcdabxabcdabcdabcy', 'abcdabcy')).toEqual([15]); 12 | expect(zAlgorithm('abcxabcdabxabcdabcdabcy', 'abcdabca')).toEqual([]); 13 | expect(zAlgorithm('abcxabcdabxaabcdabcabcdabcdabcy', 'abcdabca')).toEqual([12]); 14 | expect(zAlgorithm('abcxabcdabxaabaabaaaabcdabcdabcy', 'aabaabaaa')).toEqual([11]); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/jump-game/__test__/dpTopDownJumpGame.test.js: -------------------------------------------------------------------------------- 1 | import dpTopDownJumpGame from '../dpTopDownJumpGame'; 2 | 3 | describe('dpTopDownJumpGame', () => { 4 | it('should solve Jump Game problem in top-down dynamic programming manner', () => { 5 | expect(dpTopDownJumpGame([1, 0])).toBeTruthy(); 6 | expect(dpTopDownJumpGame([100, 0])).toBeTruthy(); 7 | expect(dpTopDownJumpGame([2, 3, 1, 1, 4])).toBeTruthy(); 8 | expect(dpTopDownJumpGame([1, 1, 1, 1, 1])).toBeTruthy(); 9 | expect(dpTopDownJumpGame([1, 1, 1, 10, 1])).toBeTruthy(); 10 | expect(dpTopDownJumpGame([1, 5, 2, 1, 0, 2, 0])).toBeTruthy(); 11 | 12 | expect(dpTopDownJumpGame([1, 0, 1])).toBeFalsy(); 13 | expect(dpTopDownJumpGame([3, 2, 1, 0, 4])).toBeFalsy(); 14 | expect(dpTopDownJumpGame([0, 0, 0, 0, 0])).toBeFalsy(); 15 | expect(dpTopDownJumpGame([5, 4, 3, 2, 1, 0, 0])).toBeFalsy(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/jump-game/__test__/dpBottomUpJumpGame.test.js: -------------------------------------------------------------------------------- 1 | import dpBottomUpJumpGame from '../dpBottomUpJumpGame'; 2 | 3 | describe('dpBottomUpJumpGame', () => { 4 | it('should solve Jump Game problem in bottom-up dynamic programming manner', () => { 5 | expect(dpBottomUpJumpGame([1, 0])).toBeTruthy(); 6 | expect(dpBottomUpJumpGame([100, 0])).toBeTruthy(); 7 | expect(dpBottomUpJumpGame([2, 3, 1, 1, 4])).toBeTruthy(); 8 | expect(dpBottomUpJumpGame([1, 1, 1, 1, 1])).toBeTruthy(); 9 | expect(dpBottomUpJumpGame([1, 1, 1, 10, 1])).toBeTruthy(); 10 | expect(dpBottomUpJumpGame([1, 5, 2, 1, 0, 2, 0])).toBeTruthy(); 11 | 12 | expect(dpBottomUpJumpGame([1, 0, 1])).toBeFalsy(); 13 | expect(dpBottomUpJumpGame([3, 2, 1, 0, 4])).toBeFalsy(); 14 | expect(dpBottomUpJumpGame([0, 0, 0, 0, 0])).toBeFalsy(); 15 | expect(dpBottomUpJumpGame([5, 4, 3, 2, 1, 0, 0])).toBeFalsy(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/algorithms/search/linear-search/README.md: -------------------------------------------------------------------------------- 1 | # Linear Search 2 | In computer science, linear search or sequential search is a 3 | method for finding a target value within a list. It sequentially 4 | checks each element of the list for the target value until a 5 | match is found or until all the elements have been searched. 6 | Linear search runs in at worst linear time and makes at most `n` 7 | comparisons, where `n` is the length of the list. 8 | 9 | ![Linear Search](https://www.tutorialspoint.com/data_structures_algorithms/images/linear_search.gif) 10 | 11 | ## Complexity 12 | 13 | **Time Complexity**: `O(n)` - since in worst case we're checking each element 14 | exactly once. 15 | 16 | ## References 17 | - [Wikipedia](https://en.wikipedia.org/wiki/Linear_search) 18 | - [TutorialsPoint](https://www.tutorialspoint.com/data_structures_algorithms/linear_search_algorithm.htm) 19 | - [Youtube](https://www.youtube.com/watch?v=SGU9duLE30w) 20 | -------------------------------------------------------------------------------- /src/algorithms/string/hamming-distance/README.md: -------------------------------------------------------------------------------- 1 | # Hamming Distance 2 | 3 | the Hamming distance between two strings of equal length is the 4 | number of positions at which the corresponding symbols are 5 | different. In other words, it measures the minimum number of 6 | substitutions required to change one string into the other, or 7 | the minimum number of errors that could have transformed one 8 | string into the other. In a more general context, the Hamming 9 | distance is one of several string metrics for measuring the 10 | edit distance between two sequences. 11 | 12 | ## Examples 13 | 14 | The Hamming distance between: 15 | 16 | - "ka**rol**in" and "ka**thr**in" is **3**. 17 | - "k**a**r**ol**in" and "k**e**r**st**in" is **3**. 18 | - 10**1**1**1**01 and 10**0**1**0**01 is **2**. 19 | - 2**17**3**8**96 and 2**23**3**7**96 is **3**. 20 | 21 | ## References 22 | 23 | [Wikipedia](https://en.wikipedia.org/wiki/Hamming_distance) 24 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/jump-game/__test__/backtrackingJumpGame.test.js: -------------------------------------------------------------------------------- 1 | import backtrackingJumpGame from '../backtrackingJumpGame'; 2 | 3 | describe('backtrackingJumpGame', () => { 4 | it('should solve Jump Game problem in backtracking manner', () => { 5 | expect(backtrackingJumpGame([1, 0])).toBeTruthy(); 6 | expect(backtrackingJumpGame([100, 0])).toBeTruthy(); 7 | expect(backtrackingJumpGame([2, 3, 1, 1, 4])).toBeTruthy(); 8 | expect(backtrackingJumpGame([1, 1, 1, 1, 1])).toBeTruthy(); 9 | expect(backtrackingJumpGame([1, 1, 1, 10, 1])).toBeTruthy(); 10 | expect(backtrackingJumpGame([1, 5, 2, 1, 0, 2, 0])).toBeTruthy(); 11 | 12 | expect(backtrackingJumpGame([1, 0, 1])).toBeFalsy(); 13 | expect(backtrackingJumpGame([3, 2, 1, 0, 4])).toBeFalsy(); 14 | expect(backtrackingJumpGame([0, 0, 0, 0, 0])).toBeFalsy(); 15 | expect(backtrackingJumpGame([5, 4, 3, 2, 1, 0, 0])).toBeFalsy(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // The bail config option can be used here to have Jest stop running tests after 3 | // the first failure. 4 | bail: false, 5 | 6 | // Indicates whether each individual test should be reported during the run. 7 | verbose: false, 8 | 9 | // Indicates whether the coverage information should be collected while executing the test 10 | collectCoverage: false, 11 | 12 | // The directory where Jest should output its coverage files. 13 | coverageDirectory: './coverage/', 14 | 15 | // If the test path matches any of the patterns, it will be skipped. 16 | testPathIgnorePatterns: ['/node_modules/'], 17 | 18 | // If the file path matches any of the patterns, coverage information will be skipped. 19 | coveragePathIgnorePatterns: ['/node_modules/'], 20 | 21 | // The pattern Jest uses to detect test files. 22 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.jsx?$', 23 | }; 24 | -------------------------------------------------------------------------------- /src/algorithms/string/hamming-distance/__test__/hammingDistance.test.js: -------------------------------------------------------------------------------- 1 | import hammingDistance from '../hammingDistance'; 2 | 3 | describe('hammingDistance', () => { 4 | it('should throw an error when trying to compare the strings of different lengths', () => { 5 | const compareStringsOfDifferentLength = () => { 6 | hammingDistance('a', 'aa'); 7 | }; 8 | 9 | expect(compareStringsOfDifferentLength).toThrowError(); 10 | }); 11 | 12 | it('should calculate difference between two strings', () => { 13 | expect(hammingDistance('a', 'a')).toBe(0); 14 | expect(hammingDistance('a', 'b')).toBe(1); 15 | expect(hammingDistance('abc', 'add')).toBe(2); 16 | expect(hammingDistance('karolin', 'kathrin')).toBe(3); 17 | expect(hammingDistance('karolin', 'kerstin')).toBe(3); 18 | expect(hammingDistance('1011101', '1001001')).toBe(2); 19 | expect(hammingDistance('2173896', '2233796')).toBe(3); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/data-structures/graph/GraphEdge.js: -------------------------------------------------------------------------------- 1 | export default class GraphEdge { 2 | /** 3 | * @param {GraphVertex} startVertex 4 | * @param {GraphVertex} endVertex 5 | * @param {number} [weight=1] 6 | */ 7 | constructor(startVertex, endVertex, weight = 0) { 8 | this.startVertex = startVertex; 9 | this.endVertex = endVertex; 10 | this.weight = weight; 11 | } 12 | 13 | /** 14 | * @return {string} 15 | */ 16 | getKey() { 17 | const startVertexKey = this.startVertex.getKey(); 18 | const endVertexKey = this.endVertex.getKey(); 19 | 20 | return `${startVertexKey}_${endVertexKey}`; 21 | } 22 | 23 | /** 24 | * @return {GraphEdge} 25 | */ 26 | reverse() { 27 | const tmp = this.startVertex; 28 | this.startVertex = this.endVertex; 29 | this.endVertex = tmp; 30 | 31 | return this; 32 | } 33 | 34 | /** 35 | * @return {string} 36 | */ 37 | toString() { 38 | return this.getKey(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/algorithms/math/fibonacci/README.md: -------------------------------------------------------------------------------- 1 | # Fibonacci Number 2 | 3 | In mathematics, the Fibonacci numbers are the numbers in the following 4 | integer sequence, called the Fibonacci sequence, and characterized by 5 | the fact that every number after the first two is the sum of the two 6 | preceding ones: 7 | 8 | `0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...` 9 | 10 | A tiling with squares whose side lengths are successive Fibonacci numbers 11 | 12 | ![Fibonacci](https://upload.wikimedia.org/wikipedia/commons/d/db/34%2A21-FibonacciBlocks.png) 13 | 14 | The Fibonacci spiral: an approximation of the golden spiral created by drawing circular arcs connecting the opposite corners of squares in the Fibonacci tiling;[4] this one uses squares of sizes 1, 1, 2, 3, 5, 8, 13 and 21. 15 | 16 | ![Fibonacci Spiral](https://upload.wikimedia.org/wikipedia/commons/2/2e/FibonacciSpiral.svg) 17 | 18 | ## References 19 | 20 | [Wikipedia](https://en.wikipedia.org/wiki/Fibonacci_number) 21 | -------------------------------------------------------------------------------- /src/algorithms/sets/maximum-subarray/bfMaximumSubarray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Brute Force solution. 3 | * Complexity: O(n^2) 4 | * 5 | * @param {Number[]} inputArray 6 | * @return {Number[]} 7 | */ 8 | export default function bfMaximumSubarray(inputArray) { 9 | let maxSubarrayStartIndex = 0; 10 | let maxSubarrayLength = 0; 11 | let maxSubarraySum = null; 12 | 13 | for (let startIndex = 0; startIndex < inputArray.length; startIndex += 1) { 14 | let subarraySum = 0; 15 | for (let arrLength = 1; arrLength <= (inputArray.length - startIndex); arrLength += 1) { 16 | subarraySum += inputArray[startIndex + (arrLength - 1)]; 17 | if (maxSubarraySum === null || subarraySum > maxSubarraySum) { 18 | maxSubarraySum = subarraySum; 19 | maxSubarrayStartIndex = startIndex; 20 | maxSubarrayLength = arrLength; 21 | } 22 | } 23 | } 24 | 25 | return inputArray.slice(maxSubarrayStartIndex, maxSubarrayStartIndex + maxSubarrayLength); 26 | } 27 | -------------------------------------------------------------------------------- /src/algorithms/sets/permutations/permutateWithRepetitions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {*[]} permutationOptions 3 | * @param {number} permutationLength 4 | * @return {*[]} 5 | */ 6 | export default function permutateWithRepetitions( 7 | permutationOptions, 8 | permutationLength = permutationOptions.length, 9 | ) { 10 | if (permutationLength === 1) { 11 | return permutationOptions.map(permutationOption => [permutationOption]); 12 | } 13 | 14 | // Init permutations array. 15 | const permutations = []; 16 | 17 | // Go through all options and join it to the smaller permutations. 18 | permutationOptions.forEach((currentOption) => { 19 | const smallerPermutations = permutateWithRepetitions( 20 | permutationOptions, 21 | permutationLength - 1, 22 | ); 23 | 24 | smallerPermutations.forEach((smallerPermutation) => { 25 | permutations.push([currentOption].concat(smallerPermutation)); 26 | }); 27 | }); 28 | 29 | return permutations; 30 | } 31 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/knight-tour/__test__/knightTour.test.js: -------------------------------------------------------------------------------- 1 | import knightTour from '../knightTour'; 2 | 3 | describe('knightTour', () => { 4 | it('should not find solution on 3x3 board', () => { 5 | const moves = knightTour(3); 6 | 7 | expect(moves.length).toBe(0); 8 | }); 9 | 10 | it('should find one solution to do knight tour on 5x5 board', () => { 11 | const moves = knightTour(5); 12 | 13 | expect(moves.length).toBe(25); 14 | 15 | expect(moves).toEqual([ 16 | [0, 0], 17 | [1, 2], 18 | [2, 0], 19 | [0, 1], 20 | [1, 3], 21 | [3, 4], 22 | [2, 2], 23 | [4, 1], 24 | [3, 3], 25 | [1, 4], 26 | [0, 2], 27 | [1, 0], 28 | [3, 1], 29 | [4, 3], 30 | [2, 4], 31 | [0, 3], 32 | [1, 1], 33 | [3, 0], 34 | [4, 2], 35 | [2, 1], 36 | [4, 0], 37 | [3, 2], 38 | [4, 4], 39 | [2, 3], 40 | [0, 4], 41 | ]); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/algorithms/math/primality-test/trialDivision.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @return {boolean} 4 | */ 5 | export default function trialDivision(number) { 6 | // Check if number is integer. 7 | if (number % 1 !== 0) { 8 | return false; 9 | } 10 | 11 | if (number <= 1) { 12 | // If number is less than one then it isn't prime by definition. 13 | return false; 14 | } 15 | 16 | if (number <= 3) { 17 | // All numbers from 2 to 3 are prime. 18 | return true; 19 | } 20 | 21 | // If the number is not divided by 2 then we may eliminate all further even dividers. 22 | if (number % 2 === 0) { 23 | return false; 24 | } 25 | 26 | // If there is no dividers up to square root of n then there is no higher dividers as well. 27 | const dividerLimit = Math.sqrt(number); 28 | for (let divider = 3; divider <= dividerLimit; divider += 2) { 29 | if (number % divider === 0) { 30 | return false; 31 | } 32 | } 33 | 34 | return true; 35 | } 36 | -------------------------------------------------------------------------------- /src/algorithms/sorting/heap-sort/HeapSort.js: -------------------------------------------------------------------------------- 1 | import Sort from '../Sort'; 2 | import MinHeap from '../../../data-structures/heap/MinHeap'; 3 | 4 | export default class HeapSort extends Sort { 5 | sort(originalArray) { 6 | const sortedArray = []; 7 | const minHeap = new MinHeap(this.callbacks.compareCallback); 8 | 9 | // Insert all array elements to the heap. 10 | originalArray.forEach((element) => { 11 | // Call visiting callback. 12 | this.callbacks.visitingCallback(element); 13 | 14 | minHeap.add(element); 15 | }); 16 | 17 | // Now we have min heap with minimal element always on top. 18 | // Let's poll that minimal element one by one and thus form the sorted array. 19 | while (!minHeap.isEmpty()) { 20 | const nextMinElement = minHeap.poll(); 21 | 22 | // Call visiting callback. 23 | this.callbacks.visitingCallback(nextMinElement); 24 | 25 | sortedArray.push(nextMinElement); 26 | } 27 | 28 | return sortedArray; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/algorithms/sorting/radix-sort/__test__/RadixSort.test.js: -------------------------------------------------------------------------------- 1 | import RadixSort from '../RadixSort'; 2 | import { SortTester } from '../../SortTester'; 3 | 4 | // Complexity constants. 5 | const ARRAY_OF_STRINGS_VISIT_COUNT = 24; 6 | const ARRAY_OF_INTEGERS_VISIT_COUNT = 77; 7 | describe('RadixSort', () => { 8 | it('should sort array', () => { 9 | SortTester.testSort(RadixSort); 10 | }); 11 | 12 | it('should visit array of strings n (number of strings) x m (length of longest element) times', () => { 13 | SortTester.testAlgorithmTimeComplexity( 14 | RadixSort, 15 | ['zzz', 'bb', 'a', 'rr', 'rrb', 'rrba'], 16 | ARRAY_OF_STRINGS_VISIT_COUNT, 17 | ); 18 | }); 19 | 20 | it('should visit array of integers n (number of elements) x m (length of longest integer) times', () => { 21 | SortTester.testAlgorithmTimeComplexity( 22 | RadixSort, 23 | [3, 1, 75, 32, 884, 523, 4343456, 232, 123, 656, 343], 24 | ARRAY_OF_INTEGERS_VISIT_COUNT, 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js: -------------------------------------------------------------------------------- 1 | import isPowerOfTwo from '../isPowerOfTwo'; 2 | 3 | describe('isPowerOfTwo', () => { 4 | it('should check if the number is made by multiplying twos', () => { 5 | expect(isPowerOfTwo(-1)).toBeFalsy(); 6 | expect(isPowerOfTwo(0)).toBeFalsy(); 7 | expect(isPowerOfTwo(1)).toBeTruthy(); 8 | expect(isPowerOfTwo(2)).toBeTruthy(); 9 | expect(isPowerOfTwo(3)).toBeFalsy(); 10 | expect(isPowerOfTwo(4)).toBeTruthy(); 11 | expect(isPowerOfTwo(5)).toBeFalsy(); 12 | expect(isPowerOfTwo(6)).toBeFalsy(); 13 | expect(isPowerOfTwo(7)).toBeFalsy(); 14 | expect(isPowerOfTwo(8)).toBeTruthy(); 15 | expect(isPowerOfTwo(10)).toBeFalsy(); 16 | expect(isPowerOfTwo(12)).toBeFalsy(); 17 | expect(isPowerOfTwo(16)).toBeTruthy(); 18 | expect(isPowerOfTwo(31)).toBeFalsy(); 19 | expect(isPowerOfTwo(64)).toBeTruthy(); 20 | expect(isPowerOfTwo(1024)).toBeTruthy(); 21 | expect(isPowerOfTwo(1023)).toBeFalsy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/algorithms/sets/maximum-subarray/README.md: -------------------------------------------------------------------------------- 1 | # Maximum subarray problem 2 | 3 | The maximum subarray problem is the task of finding the contiguous 4 | subarray within a one-dimensional array, `a[1...n]`, of numbers 5 | which has the largest sum, where, 6 | 7 | ![Maximum subarray](https://wikimedia.org/api/rest_v1/media/math/render/svg/e8960f093107b71b21827e726e2bad8b023779b2) 8 | 9 | ![Maximum subarray](https://www.geeksforgeeks.org/wp-content/uploads/kadane-Algorithm.png) 10 | 11 | ## Example 12 | 13 | The list usually contains both positive and negative numbers along 14 | with `0`. For example, for the array of 15 | values `−2, 1, −3, 4, −1, 2, 1, −5, 4` the contiguous subarray 16 | with the largest sum is `4, −1, 2, 1`, with sum `6`. 17 | 18 | ## References 19 | 20 | - [Wikipedia](https://en.wikipedia.org/wiki/Maximum_subarray_problem) 21 | - [YouTube](https://www.youtube.com/watch?v=ohHWQf1HDfU&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 22 | - [GeeksForGeeks](https://www.geeksforgeeks.org/largest-sum-contiguous-subarray/) 23 | -------------------------------------------------------------------------------- /src/algorithms/sorting/insertion-sort/README.md: -------------------------------------------------------------------------------- 1 | # Insertion Sort 2 | 3 | Insertion sort is a simple sorting algorithm that builds 4 | the final sorted array (or list) one item at a time. 5 | It is much less efficient on large lists than more 6 | advanced algorithms such as quicksort, heapsort, or merge 7 | sort. 8 | 9 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/4/42/Insertion_sort.gif) 10 | 11 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/0/0f/Insertion-sort-example-300px.gif) 12 | 13 | ## Complexity 14 | 15 | | Name | Best | Average | Worst | Memory | Stable | Comments | 16 | | --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- | 17 | | **Insertion sort** | n | n2 | n2 | 1 | Yes | | 18 | 19 | ## References 20 | 21 | [Wikipedia](https://en.wikipedia.org/wiki/Insertion_sort) 22 | -------------------------------------------------------------------------------- /src/data-structures/priority-queue/README.md: -------------------------------------------------------------------------------- 1 | # Priority Queue 2 | 3 | In computer science, a priority queue is an abstract data type 4 | which is like a regular queue or stack data structure, but where 5 | additionally each element has a "priority" associated with it. 6 | In a priority queue, an element with high priority is served before 7 | an element with low priority. If two elements have the same 8 | priority, they are served according to their order in the queue. 9 | 10 | While priority queues are often implemented with heaps, they are 11 | conceptually distinct from heaps. A priority queue is an abstract 12 | concept like "a list" or "a map"; just as a list can be implemented 13 | with a linked list or an array, a priority queue can be implemented 14 | with a heap or a variety of other methods such as an unordered 15 | array. 16 | 17 | ## References 18 | 19 | - [Wikipedia](https://en.wikipedia.org/wiki/Priority_queue) 20 | - [YouTube](https://www.youtube.com/watch?v=wptevk0bshY&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=6) 21 | -------------------------------------------------------------------------------- /src/algorithms/search/binary-search/README.md: -------------------------------------------------------------------------------- 1 | # Binary Search 2 | 3 | In computer science, binary search, also known as half-interval 4 | search, logarithmic search, or binary chop, is a search algorithm 5 | that finds the position of a target value within a sorted 6 | array. Binary search compares the target value to the middle 7 | element of the array; if they are unequal, the half in which 8 | the target cannot lie is eliminated and the search continues 9 | on the remaining half until it is successful. If the search 10 | ends with the remaining half being empty, the target is not 11 | in the array. 12 | 13 | ![Binary Search](https://upload.wikimedia.org/wikipedia/commons/8/83/Binary_Search_Depiction.svg) 14 | 15 | ## Complexity 16 | 17 | **Time Complexity**: `O(log(n))` - since we split search area by two for every 18 | next iteration. 19 | 20 | ## References 21 | 22 | - [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_algorithm) 23 | - [YouTube](https://www.youtube.com/watch?v=P3YID7liBug&index=29&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 24 | -------------------------------------------------------------------------------- /src/data-structures/tree/binary-search-tree/BinarySearchTree.js: -------------------------------------------------------------------------------- 1 | import BinarySearchTreeNode from './BinarySearchTreeNode'; 2 | 3 | export default class BinarySearchTree { 4 | /** 5 | * @param {function} [nodeValueCompareFunction] 6 | */ 7 | constructor(nodeValueCompareFunction) { 8 | this.root = new BinarySearchTreeNode(null, nodeValueCompareFunction); 9 | 10 | // Steal node comparator from the root. 11 | this.nodeComparator = this.root.nodeComparator; 12 | } 13 | 14 | /** 15 | * @param {*} value 16 | * @return {BinarySearchTreeNode} 17 | */ 18 | insert(value) { 19 | return this.root.insert(value); 20 | } 21 | 22 | /** 23 | * @param {*} value 24 | * @return {boolean} 25 | */ 26 | contains(value) { 27 | return this.root.contains(value); 28 | } 29 | 30 | /** 31 | * @param {*} value 32 | * @return {boolean} 33 | */ 34 | remove(value) { 35 | return this.root.remove(value); 36 | } 37 | 38 | /** 39 | * @return {string} 40 | */ 41 | toString() { 42 | return this.root.toString(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/algorithms/sorting/selection-sort/SelectionSort.js: -------------------------------------------------------------------------------- 1 | import Sort from '../Sort'; 2 | 3 | export default class SelectionSort extends Sort { 4 | sort(originalArray) { 5 | // Clone original array to prevent its modification. 6 | const array = [...originalArray]; 7 | 8 | for (let i = 0; i < array.length - 1; i += 1) { 9 | let minIndex = i; 10 | 11 | // Call visiting callback. 12 | this.callbacks.visitingCallback(array[i]); 13 | 14 | // Find minimum element in the rest of array. 15 | for (let j = i + 1; j < array.length; j += 1) { 16 | // Call visiting callback. 17 | this.callbacks.visitingCallback(array[j]); 18 | 19 | if (this.comparator.lessThan(array[j], array[minIndex])) { 20 | minIndex = j; 21 | } 22 | } 23 | 24 | // If new minimum element has been found then swap it with current i-th element. 25 | if (minIndex !== i) { 26 | const tmp = array[i]; 27 | array[i] = array[minIndex]; 28 | array[minIndex] = tmp; 29 | } 30 | } 31 | 32 | return array; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/comparator/Comparator.js: -------------------------------------------------------------------------------- 1 | export default class Comparator { 2 | /** 3 | * @param {function(a: *, b: *)} [compareFunction] 4 | */ 5 | constructor(compareFunction) { 6 | this.compare = compareFunction || Comparator.defaultCompareFunction; 7 | } 8 | 9 | /** 10 | * @param {(string|number)} a 11 | * @param {(string|number)} b 12 | * @returns {number} 13 | */ 14 | static defaultCompareFunction(a, b) { 15 | if (a === b) { 16 | return 0; 17 | } 18 | 19 | return a < b ? -1 : 1; 20 | } 21 | 22 | equal(a, b) { 23 | return this.compare(a, b) === 0; 24 | } 25 | 26 | lessThan(a, b) { 27 | return this.compare(a, b) < 0; 28 | } 29 | 30 | greaterThan(a, b) { 31 | return this.compare(a, b) > 0; 32 | } 33 | 34 | lessThanOrEqual(a, b) { 35 | return this.lessThan(a, b) || this.equal(a, b); 36 | } 37 | 38 | greaterThanOrEqual(a, b) { 39 | return this.greaterThan(a, b) || this.equal(a, b); 40 | } 41 | 42 | reverse() { 43 | const compareOriginal = this.compare; 44 | this.compare = (a, b) => compareOriginal(b, a); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/algorithms/sets/knapsack-problem/KnapsackItem.js: -------------------------------------------------------------------------------- 1 | export default class KnapsackItem { 2 | /** 3 | * @param {Object} itemSettings - knapsack item settings, 4 | * @param {number} itemSettings.value - value of the item. 5 | * @param {number} itemSettings.weight - weight of the item. 6 | * @param {number} itemSettings.itemsInStock - how many items are available to be added. 7 | */ 8 | constructor({ value, weight, itemsInStock = 1 }) { 9 | this.value = value; 10 | this.weight = weight; 11 | this.itemsInStock = itemsInStock; 12 | // Actual number of items that is going to be added to knapsack. 13 | this.quantity = 1; 14 | } 15 | 16 | get totalValue() { 17 | return this.value * this.quantity; 18 | } 19 | 20 | get totalWeight() { 21 | return this.weight * this.quantity; 22 | } 23 | 24 | // This coefficient shows how valuable the 1 unit of weight is 25 | // for current item. 26 | get valuePerWeightRatio() { 27 | return this.value / this.weight; 28 | } 29 | 30 | toString() { 31 | return `v${this.value} w${this.weight} x ${this.quantity}`; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/algorithms/string/rabin-karp/README.md: -------------------------------------------------------------------------------- 1 | # Rabin Karp Algorithm 2 | 3 | In computer science, the Rabin–Karp algorithm or Karp–Rabin algorithm 4 | is a string searching algorithm created by Richard M. Karp and 5 | Michael O. Rabin (1987) that uses hashing to find any one of a set 6 | of pattern strings in a text. 7 | 8 | ## Complexity 9 | 10 | For text of length `n` and `p` patterns 11 | of combined length `m`, its average and best case running time is 12 | `O(n + m)` in space `O(p)`, but its worst-case time is `O(n * m)`. 13 | 14 | ## Application 15 | 16 | A practical application of the algorithm is detecting plagiarism. 17 | Given source material, the algorithm can rapidly search through a paper 18 | for instances of sentences from the source material, ignoring details 19 | such as case and punctuation. Because of the abundance of the sought 20 | strings, single-string searching algorithms are impractical. 21 | 22 | ## References 23 | 24 | - [Wikipedia](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) 25 | - [YouTube](https://www.youtube.com/watch?v=H4VrKHVG5qI&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 26 | -------------------------------------------------------------------------------- /src/algorithms/sorting/bubble-sort/README.md: -------------------------------------------------------------------------------- 1 | # Bubble Sort 2 | 3 | Bubble sort, sometimes referred to as sinking sort, is a 4 | simple sorting algorithm that repeatedly steps through 5 | the list to be sorted, compares each pair of adjacent 6 | items and swaps them if they are in the wrong order. 7 | The pass through the list is repeated until no swaps 8 | are needed, which indicates that the list is sorted. 9 | 10 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/c/c8/Bubble-sort-example-300px.gif) 11 | 12 | ## Complexity 13 | 14 | | Name | Best | Average | Worst | Memory | Stable | Comments | 15 | | --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- | 16 | | **Bubble sort** | n | n2 | n2 | 1 | Yes | | 17 | 18 | ## References 19 | 20 | - [Wikipedia](https://en.wikipedia.org/wiki/Bubble_sort) 21 | - [YouTube](https://www.youtube.com/watch?v=6Gv8vg0kcHc&index=27&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 22 | -------------------------------------------------------------------------------- /src/data-structures/trie/TrieNode.js: -------------------------------------------------------------------------------- 1 | import HashTable from '../hash-table/HashTable'; 2 | 3 | export default class TrieNode { 4 | constructor(character, isCompleteWord = false) { 5 | this.character = character; 6 | this.isCompleteWord = isCompleteWord; 7 | this.children = new HashTable(); 8 | } 9 | 10 | getChild(character) { 11 | return this.children.get(character); 12 | } 13 | 14 | addChild(character, isCompleteWord = false) { 15 | if (!this.children.has(character)) { 16 | this.children.set(character, new TrieNode(character, isCompleteWord)); 17 | } 18 | 19 | return this.children.get(character); 20 | } 21 | 22 | hasChild(character) { 23 | return this.children.has(character); 24 | } 25 | 26 | suggestChildren() { 27 | return [...this.children.getKeys()]; 28 | } 29 | 30 | toString() { 31 | let childrenAsString = this.suggestChildren().toString(); 32 | childrenAsString = childrenAsString ? `:${childrenAsString}` : ''; 33 | const isCompleteString = this.isCompleteWord ? '*' : ''; 34 | 35 | return `${this.character}${isCompleteString}${childrenAsString}`; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Oleksii Trekhleb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/algorithms/graph/articulation-points/README.md: -------------------------------------------------------------------------------- 1 | # Articulation Points (or Cut Vertices) 2 | 3 | A vertex in an undirected connected graph is an articulation point 4 | (or cut vertex) if removing it (and edges through it) disconnects 5 | the graph. Articulation points represent vulnerabilities in a 6 | connected network – single points whose failure would split the 7 | network into 2 or more disconnected components. They are useful for 8 | designing reliable networks. 9 | 10 | For a disconnected undirected graph, an articulation point is a 11 | vertex removing which increases number of connected components. 12 | 13 | ![Articulation Points](https://www.geeksforgeeks.org/wp-content/uploads/ArticulationPoints.png) 14 | 15 | ![Articulation Points](https://www.geeksforgeeks.org/wp-content/uploads/ArticulationPoints1.png) 16 | 17 | ![Articulation Points](https://www.geeksforgeeks.org/wp-content/uploads/ArticulationPoints21.png) 18 | 19 | ## References 20 | 21 | - [GeeksForGeeks](https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/) 22 | - [YouTube](https://www.youtube.com/watch?v=2kREIkF9UAs&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 23 | -------------------------------------------------------------------------------- /src/algorithms/sets/longest-increasing-subsequence/__test__/dpLongestIncreasingSubsequence.test.js: -------------------------------------------------------------------------------- 1 | import dpLongestIncreasingSubsequence from '../dpLongestIncreasingSubsequence'; 2 | 3 | describe('dpLongestIncreasingSubsequence', () => { 4 | it('should find longest increasing subsequence length', () => { 5 | // Should be: 6 | // 9 or 7 | // 8 or 8 | // 7 or 9 | // 6 or 10 | // ... 11 | expect(dpLongestIncreasingSubsequence([ 12 | 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 13 | ])).toBe(1); 14 | 15 | // Should be: 16 | // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 17 | expect(dpLongestIncreasingSubsequence([ 18 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 19 | ])).toBe(10); 20 | 21 | // Should be: 22 | // -1, 0, 2, 3 23 | expect(dpLongestIncreasingSubsequence([ 24 | 3, 4, -1, 0, 6, 2, 3, 25 | ])).toBe(4); 26 | 27 | // Should be: 28 | // 0, 2, 6, 9, 11, 15 or 29 | // 0, 4, 6, 9, 11, 15 or 30 | // 0, 2, 6, 9, 13, 15 or 31 | // 0, 4, 6, 9, 13, 15 32 | expect(dpLongestIncreasingSubsequence([ 33 | 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15, 34 | ])).toBe(6); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/data-structures/hash-table/README.md: -------------------------------------------------------------------------------- 1 | # Hash Table 2 | 3 | In computing, a hash table (hash map) is a data 4 | structure which implements an associative array 5 | abstract data type, a structure that can map keys 6 | to values. A hash table uses a hash function to 7 | compute an index into an array of buckets or slots, 8 | from which the desired value can be found 9 | 10 | Ideally, the hash function will assign each key to a 11 | unique bucket, but most hash table designs employ an 12 | imperfect hash function, which might cause hash 13 | collisions where the hash function generates the same 14 | index for more than one key. Such collisions must be 15 | accommodated in some way. 16 | 17 | ![Hash Table](https://upload.wikimedia.org/wikipedia/commons/7/7d/Hash_table_3_1_1_0_1_0_0_SP.svg) 18 | 19 | Hash collision resolved by separate chaining. 20 | 21 | ![Hash Collision](https://upload.wikimedia.org/wikipedia/commons/d/d0/Hash_table_5_0_1_1_1_1_1_LL.svg) 22 | 23 | ## References 24 | 25 | - [Wikipedia](https://en.wikipedia.org/wiki/Hash_table) 26 | - [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 27 | -------------------------------------------------------------------------------- /src/algorithms/math/factorial/README.md: -------------------------------------------------------------------------------- 1 | # Factorial 2 | 3 | In mathematics, the factorial of a non-negative integer `n`, 4 | denoted by `n!`, is the product of all positive integers less 5 | than or equal to `n`. For example: 6 | 7 | ``` 8 | 5! = 5 * 4 * 3 * 2 * 1 = 120 9 | ``` 10 | 11 | | n | n! | 12 | | ----- | --------------------------: | 13 | | 0 | 1 | 14 | | 1 | 1 | 15 | | 2 | 2 | 16 | | 3 | 6 | 17 | | 4 | 24 | 18 | | 5 | 120 | 19 | | 6 | 720 | 20 | | 7 | 5 040 | 21 | | 8 | 40 320 | 22 | | 9 | 362 880 | 23 | | 10 | 3 628 800 | 24 | | 11 | 39 916 800 | 25 | | 12 | 479 001 600 | 26 | | 13 | 6 227 020 800 | 27 | | 14 | 87 178 291 200 | 28 | | 15 | 1 307 674 368 000 | 29 | 30 | ## References 31 | 32 | [Wikipedia](https://en.wikipedia.org/wiki/Factorial) 33 | -------------------------------------------------------------------------------- /src/algorithms/math/is-power-of-two/__test__/isPowerOfTwoBitwise.test.js: -------------------------------------------------------------------------------- 1 | import isPowerOfTwoBitwise from '../isPowerOfTwoBitwise'; 2 | 3 | describe('isPowerOfTwoBitwise', () => { 4 | it('should check if the number is made by multiplying twos', () => { 5 | expect(isPowerOfTwoBitwise(-1)).toBeFalsy(); 6 | expect(isPowerOfTwoBitwise(0)).toBeFalsy(); 7 | expect(isPowerOfTwoBitwise(1)).toBeTruthy(); 8 | expect(isPowerOfTwoBitwise(2)).toBeTruthy(); 9 | expect(isPowerOfTwoBitwise(3)).toBeFalsy(); 10 | expect(isPowerOfTwoBitwise(4)).toBeTruthy(); 11 | expect(isPowerOfTwoBitwise(5)).toBeFalsy(); 12 | expect(isPowerOfTwoBitwise(6)).toBeFalsy(); 13 | expect(isPowerOfTwoBitwise(7)).toBeFalsy(); 14 | expect(isPowerOfTwoBitwise(8)).toBeTruthy(); 15 | expect(isPowerOfTwoBitwise(10)).toBeFalsy(); 16 | expect(isPowerOfTwoBitwise(12)).toBeFalsy(); 17 | expect(isPowerOfTwoBitwise(16)).toBeTruthy(); 18 | expect(isPowerOfTwoBitwise(31)).toBeFalsy(); 19 | expect(isPowerOfTwoBitwise(64)).toBeTruthy(); 20 | expect(isPowerOfTwoBitwise(1024)).toBeTruthy(); 21 | expect(isPowerOfTwoBitwise(1023)).toBeFalsy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/algorithms/math/pascal-triangle/pascalTriangleRecursive.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} lineNumber - zero based. 3 | * @return {number[]} 4 | */ 5 | export default function pascalTriangleRecursive(lineNumber) { 6 | if (lineNumber === 0) { 7 | return [1]; 8 | } 9 | 10 | const currentLineSize = lineNumber + 1; 11 | const previousLineSize = currentLineSize - 1; 12 | 13 | // Create container for current line values. 14 | const currentLine = []; 15 | 16 | // We'll calculate current line based on previous one. 17 | const previousLine = pascalTriangleRecursive(lineNumber - 1); 18 | 19 | // Let's go through all elements of current line except the first and 20 | // last one (since they were and will be filled with 1's) and calculate 21 | // current coefficient based on previous line. 22 | for (let numIndex = 0; numIndex < currentLineSize; numIndex += 1) { 23 | const leftCoefficient = (numIndex - 1) >= 0 ? previousLine[numIndex - 1] : 0; 24 | const rightCoefficient = numIndex < previousLineSize ? previousLine[numIndex] : 0; 25 | 26 | currentLine[numIndex] = leftCoefficient + rightCoefficient; 27 | } 28 | 29 | return currentLine; 30 | } 31 | -------------------------------------------------------------------------------- /src/algorithms/sets/longest-common-subsequence/__test__/longestCommonSubsequence.test.js: -------------------------------------------------------------------------------- 1 | import longestCommonSubsequence from '../longestCommonSubsequence'; 2 | 3 | describe('longestCommonSubsequence', () => { 4 | it('should find longest common subsequence for two strings', () => { 5 | expect(longestCommonSubsequence([''], [''])).toEqual(['']); 6 | 7 | expect(longestCommonSubsequence([''], ['A', 'B', 'C'])).toEqual(['']); 8 | 9 | expect(longestCommonSubsequence(['A', 'B', 'C'], [''])).toEqual(['']); 10 | 11 | expect(longestCommonSubsequence( 12 | ['A', 'B', 'C'], 13 | ['D', 'E', 'F', 'G'], 14 | )).toEqual(['']); 15 | 16 | expect(longestCommonSubsequence( 17 | ['A', 'B', 'C', 'D', 'G', 'H'], 18 | ['A', 'E', 'D', 'F', 'H', 'R'], 19 | )).toEqual(['A', 'D', 'H']); 20 | 21 | expect(longestCommonSubsequence( 22 | ['A', 'G', 'G', 'T', 'A', 'B'], 23 | ['G', 'X', 'T', 'X', 'A', 'Y', 'B'], 24 | )).toEqual(['G', 'T', 'A', 'B']); 25 | 26 | expect(longestCommonSubsequence( 27 | ['A', 'B', 'C', 'D', 'A', 'F'], 28 | ['A', 'C', 'B', 'C', 'F'], 29 | )).toEqual(['A', 'B', 'C', 'F']); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/algorithms/sorting/insertion-sort/InsertionSort.js: -------------------------------------------------------------------------------- 1 | import Sort from '../Sort'; 2 | 3 | export default class InsertionSort extends Sort { 4 | sort(originalArray) { 5 | const array = [...originalArray]; 6 | 7 | // Go through all array elements... 8 | for (let i = 0; i < array.length; i += 1) { 9 | let currentIndex = i; 10 | 11 | // Call visiting callback. 12 | this.callbacks.visitingCallback(array[i]); 13 | 14 | // Go and check if previous elements and greater then current one. 15 | // If this is the case then swap that elements. 16 | while ( 17 | array[currentIndex - 1] !== undefined 18 | && this.comparator.lessThan(array[currentIndex], array[currentIndex - 1]) 19 | ) { 20 | // Call visiting callback. 21 | this.callbacks.visitingCallback(array[currentIndex - 1]); 22 | 23 | // Swap the elements. 24 | const tmp = array[currentIndex - 1]; 25 | array[currentIndex - 1] = array[currentIndex]; 26 | array[currentIndex] = tmp; 27 | 28 | // Shift current index left. 29 | currentIndex -= 1; 30 | } 31 | } 32 | 33 | return array; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/algorithms/math/sieve-of-eratosthenes/sieveOfEratosthenes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} maxNumber 3 | * @return {number[]} 4 | */ 5 | export default function sieveOfEratosthenes(maxNumber) { 6 | const isPrime = new Array(maxNumber + 1).fill(true); 7 | isPrime[0] = false; 8 | isPrime[1] = false; 9 | 10 | const primes = []; 11 | 12 | for (let number = 2; number <= maxNumber; number += 1) { 13 | if (isPrime[number] === true) { 14 | primes.push(number); 15 | 16 | /* 17 | * Optimisation. 18 | * Start marking multiples of `p` from `p * p`, and not from `2 * p`. 19 | * The reason why this works is because, at that point, smaller multiples 20 | * of `p` will have already been marked `false`. 21 | * 22 | * Warning: When working with really big numbers, the following line may cause overflow 23 | * In that case, it can be changed to: 24 | * let nextNumber = 2 * number; 25 | */ 26 | let nextNumber = number * number; 27 | 28 | while (nextNumber <= maxNumber) { 29 | isPrime[nextNumber] = false; 30 | nextNumber += number; 31 | } 32 | } 33 | } 34 | 35 | return primes; 36 | } 37 | -------------------------------------------------------------------------------- /src/algorithms/string/rabin-karp/__test__/rabinKarp.test.js: -------------------------------------------------------------------------------- 1 | import { rabinKarp, hashWord, reHashWord } from '../rabinKarp'; 2 | 3 | describe('rabinKarp', () => { 4 | it('should correctly calculates hash and re-hash', () => { 5 | expect(hashWord('a')).toBe(97); 6 | expect(hashWord('b')).toBe(98); 7 | expect(hashWord('abc')).toBe(941094); 8 | expect(hashWord('bcd')).toBe(950601); 9 | expect(reHashWord(hashWord('abc'), 'abc', 'bcd')).toBe(950601); 10 | expect(reHashWord(hashWord('abc'), 'abc', 'bcd')).toBe(hashWord('bcd')); 11 | }); 12 | 13 | it('should find substring in a string', () => { 14 | expect(rabinKarp('', '')).toBe(0); 15 | expect(rabinKarp('a', '')).toBe(0); 16 | expect(rabinKarp('a', 'a')).toBe(0); 17 | expect(rabinKarp('abcbcglx', 'abca')).toBe(-1); 18 | expect(rabinKarp('abcbcglx', 'bcgl')).toBe(3); 19 | expect(rabinKarp('abcxabcdabxabcdabcdabcy', 'abcdabcy')).toBe(15); 20 | expect(rabinKarp('abcxabcdabxabcdabcdabcy', 'abcdabca')).toBe(-1); 21 | expect(rabinKarp('abcxabcdabxaabcdabcabcdabcdabcy', 'abcdabca')).toBe(12); 22 | expect(rabinKarp('abcxabcdabxaabaabaaaabcdabcdabcy', 'aabaabaaa')).toBe(11); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/data-structures/disjoint-set/README.md: -------------------------------------------------------------------------------- 1 | # Disjoint Set 2 | 3 | **Disjoint-set** data structure (also called a union–find data structure or merge–find set) is a data 4 | structure that tracks a set of elements partitioned into a number of disjoint (non-overlapping) subsets. 5 | It provides near-constant-time operations (bounded by the inverse Ackermann function) to *add new sets*, 6 | to *merge existing sets*, and to *determine whether elements are in the same set*. 7 | In addition to many other uses (see the Applications section), disjoint-sets play a key role in Kruskal's algorithm for finding the minimum spanning tree of a graph. 8 | 9 | ![disjoint set](https://upload.wikimedia.org/wikipedia/commons/6/67/Dsu_disjoint_sets_init.svg) 10 | 11 | *MakeSet* creates 8 singletons. 12 | 13 | ![disjoint set](https://upload.wikimedia.org/wikipedia/commons/a/ac/Dsu_disjoint_sets_final.svg) 14 | 15 | After some operations of *Union*, some sets are grouped together. 16 | 17 | ## References 18 | 19 | - [Wikipedia](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) 20 | - [By Abdul Bari on YouTube](https://www.youtube.com/watch?v=wU6udHRIkcc&index=14&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 21 | -------------------------------------------------------------------------------- /src/data-structures/stack/Stack.js: -------------------------------------------------------------------------------- 1 | import LinkedList from '../linked-list/LinkedList'; 2 | 3 | export default class Stack { 4 | constructor() { 5 | this.linkedList = new LinkedList(); 6 | } 7 | 8 | /** 9 | * @return {boolean} 10 | */ 11 | isEmpty() { 12 | return !this.linkedList.tail; 13 | } 14 | 15 | /** 16 | * @return {*} 17 | */ 18 | peek() { 19 | if (this.isEmpty()) { 20 | return null; 21 | } 22 | 23 | return this.linkedList.tail.value; 24 | } 25 | 26 | /** 27 | * @param {*} value 28 | */ 29 | push(value) { 30 | this.linkedList.append(value); 31 | } 32 | 33 | /** 34 | * @return {*} 35 | */ 36 | pop() { 37 | const removedTail = this.linkedList.deleteTail(); 38 | return removedTail ? removedTail.value : null; 39 | } 40 | 41 | /** 42 | * @return {*[]} 43 | */ 44 | toArray() { 45 | return this.linkedList 46 | .toArray() 47 | .map(linkedListNode => linkedListNode.value) 48 | .reverse(); 49 | } 50 | 51 | /** 52 | * @param {function} [callback] 53 | * @return {string} 54 | */ 55 | toString(callback) { 56 | return this.linkedList.toString(callback); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/algorithms/sets/longest-common-subsequence/README.md: -------------------------------------------------------------------------------- 1 | # Longest common subsequence problem 2 | 3 | The longest common subsequence (LCS) problem is the problem of finding 4 | the longest subsequence common to all sequences in a set of sequences 5 | (often just two sequences). It differs from the longest common substring 6 | problem: unlike substrings, subsequences are not required to occupy 7 | consecutive positions within the original sequences. 8 | 9 | ## Application 10 | 11 | The longest common subsequence problem is a classic computer science 12 | problem, the basis of data comparison programs such as the diff utility, 13 | and has applications in bioinformatics. It is also widely used by 14 | revision control systems such as Git for reconciling multiple changes 15 | made to a revision-controlled collection of files. 16 | 17 | ## Example 18 | 19 | - LCS for input Sequences `ABCDGH` and `AEDFHR` is `ADH` of length 3. 20 | - LCS for input Sequences `AGGTAB` and `GXTXAYB` is `GTAB` of length 4. 21 | 22 | ## References 23 | 24 | - [Wikipedia](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem) 25 | - [YouTube](https://www.youtube.com/watch?v=NnD96abizww&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 26 | -------------------------------------------------------------------------------- /src/algorithms/sorting/heap-sort/README.md: -------------------------------------------------------------------------------- 1 | # Heap Sort 2 | 3 | Heapsort is a comparison-based sorting algorithm. 4 | Heapsort can be thought of as an improved selection 5 | sort: like that algorithm, it divides its input into 6 | a sorted and an unsorted region, and it iteratively 7 | shrinks the unsorted region by extracting the largest 8 | element and moving that to the sorted region. The 9 | improvement consists of the use of a heap data structure 10 | rather than a linear-time search to find the maximum. 11 | 12 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/1/1b/Sorting_heapsort_anim.gif) 13 | 14 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/4/4d/Heapsort-example.gif) 15 | 16 | ## Complexity 17 | 18 | | Name | Best | Average | Worst | Memory | Stable | Comments | 19 | | --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- | 20 | | **Heap sort** | n log(n) | n log(n) | n log(n) | 1 | No | | 21 | 22 | ## References 23 | 24 | [Wikipedia](https://en.wikipedia.org/wiki/Heapsort) 25 | -------------------------------------------------------------------------------- /src/algorithms/sorting/selection-sort/README.md: -------------------------------------------------------------------------------- 1 | # Selection Sort 2 | 3 | Selection sort is a sorting algorithm, specifically an 4 | in-place comparison sort. It has O(n2) time complexity, 5 | making it inefficient on large lists, and generally 6 | performs worse than the similar insertion sort. 7 | Selection sort is noted for its simplicity, and it has 8 | performance advantages over more complicated algorithms 9 | in certain situations, particularly where auxiliary 10 | memory is limited. 11 | 12 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/b/b0/Selection_sort_animation.gif) 13 | 14 | ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/9/94/Selection-Sort-Animation.gif) 15 | 16 | ## Complexity 17 | 18 | | Name | Best | Average | Worst | Memory | Stable | Comments | 19 | | --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- | 20 | | **Selection sort** | n2 | n2 | n2 | 1 | No | | 21 | 22 | ## References 23 | 24 | [Wikipedia](https://en.wikipedia.org/wiki/Selection_sort) 25 | -------------------------------------------------------------------------------- /src/data-structures/stack/README.md: -------------------------------------------------------------------------------- 1 | # Stack 2 | 3 | In computer science, a stack is an abstract data type that serves 4 | as a collection of elements, with two principal operations: 5 | 6 | * **push**, which adds an element to the collection, and 7 | * **pop**, which removes the most recently added element that was not yet removed. 8 | 9 | The order in which elements come off a stack gives rise to its 10 | alternative name, LIFO (last in, first out). Additionally, a 11 | peek operation may give access to the top without modifying 12 | the stack. The name "stack" for this type of structure comes 13 | from the analogy to a set of physical items stacked on top of 14 | each other, which makes it easy to take an item off the top 15 | of the stack, while getting to an item deeper in the stack 16 | may require taking off multiple other items first 17 | 18 | Simple representation of a stack runtime with push and pop operations. 19 | 20 | ![Stack](https://upload.wikimedia.org/wikipedia/commons/b/b4/Lifo_stack.png) 21 | 22 | ## References 23 | 24 | - [Wikipedia](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) 25 | - [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&) 26 | -------------------------------------------------------------------------------- /src/algorithms/sorting/Sort.js: -------------------------------------------------------------------------------- 1 | import Comparator from '../../utils/comparator/Comparator'; 2 | 3 | /** 4 | * @typedef {Object} SorterCallbacks 5 | * @property {function(a: *, b: *)} compareCallback - If provided then all elements comparisons 6 | * will be done through this callback. 7 | * @property {function(a: *)} visitingCallback - If provided it will be called each time the sorting 8 | * function is visiting the next element. 9 | */ 10 | 11 | export default class Sort { 12 | constructor(originalCallbacks) { 13 | this.callbacks = Sort.initSortingCallbacks(originalCallbacks); 14 | this.comparator = new Comparator(this.callbacks.compareCallback); 15 | } 16 | 17 | /** 18 | * @param {SorterCallbacks} originalCallbacks 19 | * @returns {SorterCallbacks} 20 | */ 21 | static initSortingCallbacks(originalCallbacks) { 22 | const callbacks = originalCallbacks || {}; 23 | const stubCallback = () => {}; 24 | 25 | callbacks.compareCallback = callbacks.compareCallback || undefined; 26 | callbacks.visitingCallback = callbacks.visitingCallback || stubCallback; 27 | 28 | return callbacks; 29 | } 30 | 31 | sort() { 32 | throw new Error('sort method must be implemented'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/data-structures/trie/README.md: -------------------------------------------------------------------------------- 1 | # Trie 2 | 3 | In computer science, a trie, also called digital tree and sometimes 4 | radix tree or prefix tree (as they can be searched by prefixes), 5 | is a kind of search tree—an ordered tree data structure that is 6 | used to store a dynamic set or associative array where the keys 7 | are usually strings. Unlike a binary search tree, no node in the 8 | tree stores the key associated with that node; instead, its 9 | position in the tree defines the key with which it is associated. 10 | All the descendants of a node have a common prefix of the string 11 | associated with that node, and the root is associated with the 12 | empty string. Values are not necessarily associated with every 13 | node. Rather, values tend only to be associated with leaves, 14 | and with some inner nodes that correspond to keys of interest. 15 | For the space-optimized presentation of prefix tree, see compact 16 | prefix tree. 17 | 18 | ![Trie](https://upload.wikimedia.org/wikipedia/commons/b/be/Trie_example.svg) 19 | 20 | ## References 21 | 22 | - [Wikipedia](https://en.wikipedia.org/wiki/Trie) 23 | - [YouTube](https://www.youtube.com/watch?v=zIjfhVPRZCg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=7&t=0s) 24 | -------------------------------------------------------------------------------- /src/algorithms/search/binary-search/binarySearch.js: -------------------------------------------------------------------------------- 1 | import Comparator from '../../../utils/comparator/Comparator'; 2 | 3 | /** 4 | * Binary search implementation. 5 | * 6 | * @param {*[]} sortedArray 7 | * @param {*} seekElement 8 | * @param {function(a, b)} [comparatorCallback] 9 | * @return {number} 10 | */ 11 | 12 | export default function binarySearch(sortedArray, seekElement, comparatorCallback) { 13 | const comparator = new Comparator(comparatorCallback); 14 | 15 | let startIndex = 0; 16 | let endIndex = sortedArray.length - 1; 17 | 18 | while (startIndex <= endIndex) { 19 | const middleIndex = startIndex + Math.floor((endIndex - startIndex) / 2); 20 | 21 | // If we've found the element just return its position. 22 | if (comparator.equal(sortedArray[middleIndex], seekElement)) { 23 | return middleIndex; 24 | } 25 | 26 | // Decide which half to choose for seeking next: left or right one. 27 | if (comparator.lessThan(sortedArray[middleIndex], seekElement)) { 28 | // Go to the right half of the array. 29 | startIndex = middleIndex + 1; 30 | } else { 31 | // Go to the left half of the array. 32 | endIndex = middleIndex - 1; 33 | } 34 | } 35 | 36 | return -1; 37 | } 38 | -------------------------------------------------------------------------------- /src/algorithms/math/integer-partition/README.md: -------------------------------------------------------------------------------- 1 | # Integer Partition 2 | 3 | In number theory and combinatorics, a partition of a positive 4 | integer `n`, also called an **integer partition**, is a way of 5 | writing `n` as a sum of positive integers. 6 | 7 | Two sums that differ only in the order of their summands are 8 | considered the same partition. For example, `4` can be partitioned 9 | in five distinct ways: 10 | 11 | ``` 12 | 4 13 | 3 + 1 14 | 2 + 2 15 | 2 + 1 + 1 16 | 1 + 1 + 1 + 1 17 | ``` 18 | 19 | The order-dependent composition `1 + 3` is the same partition 20 | as `3 + 1`, while the two distinct 21 | compositions `1 + 2 + 1` and `1 + 1 + 2` represent the same 22 | partition `2 + 1 + 1`. 23 | 24 | Young diagrams associated to the partitions of the positive 25 | integers `1` through `8`. They are arranged so that images 26 | under the reflection about the main diagonal of the square 27 | are conjugate partitions. 28 | 29 | ![Integer Partition](https://upload.wikimedia.org/wikipedia/commons/d/d8/Ferrer_partitioning_diagrams.svg) 30 | 31 | ## References 32 | 33 | - [Wikipedia](https://en.wikipedia.org/wiki/Partition_(number_theory)) 34 | - [YouTube](https://www.youtube.com/watch?v=ZaVM057DuzE&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 35 | -------------------------------------------------------------------------------- /src/algorithms/sorting/bubble-sort/BubbleSort.js: -------------------------------------------------------------------------------- 1 | import Sort from '../Sort'; 2 | 3 | export default class BubbleSort extends Sort { 4 | sort(originalArray) { 5 | // Flag that holds info about whether the swap has occur or not. 6 | let swapped = false; 7 | // Clone original array to prevent its modification. 8 | const array = [...originalArray]; 9 | 10 | for (let i = 1; i < array.length; i += 1) { 11 | swapped = false; 12 | 13 | // Call visiting callback. 14 | this.callbacks.visitingCallback(array[i]); 15 | 16 | for (let j = 0; j < array.length - i; j += 1) { 17 | // Call visiting callback. 18 | this.callbacks.visitingCallback(array[j]); 19 | 20 | // Swap elements if they are in wrong order. 21 | if (this.comparator.lessThan(array[j + 1], array[j])) { 22 | const tmp = array[j + 1]; 23 | array[j + 1] = array[j]; 24 | array[j] = tmp; 25 | 26 | // Register the swap. 27 | swapped = true; 28 | } 29 | } 30 | 31 | // If there were no swaps then array is already sorted and there is 32 | // no need to proceed. 33 | if (!swapped) { 34 | return array; 35 | } 36 | } 37 | 38 | return array; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/algorithms/graph/dijkstra/README.md: -------------------------------------------------------------------------------- 1 | # Dijkstra's Algorithm 2 | 3 | Dijkstra's algorithm is an algorithm for finding the shortest 4 | paths between nodes in a graph, which may represent, for example, 5 | road networks. 6 | 7 | The algorithm exists in many variants; Dijkstra's original variant 8 | found the shortest path between two nodes, but a more common 9 | variant fixes a single node as the "source" node and finds 10 | shortest paths from the source to all other nodes in the graph, 11 | producing a shortest-path tree. 12 | 13 | ![Dijkstra](https://upload.wikimedia.org/wikipedia/commons/5/57/Dijkstra_Animation.gif) 14 | 15 | Dijkstra's algorithm to find the shortest path between `a` and `b`. 16 | It picks the unvisited vertex with the lowest distance, 17 | calculates the distance through it to each unvisited neighbor, 18 | and updates the neighbor's distance if smaller. Mark visited 19 | (set to red) when done with neighbors. 20 | 21 | ## References 22 | 23 | - [Wikipedia](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) 24 | - [On YouTube by Nathaniel Fan](https://www.youtube.com/watch?v=gdmfOwyQlcI&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 25 | - [On YouTube by Tushar Roy](https://www.youtube.com/watch?v=lAXZGERcDf4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 26 | -------------------------------------------------------------------------------- /src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js: -------------------------------------------------------------------------------- 1 | import euclideanAlgorithm from '../euclideanAlgorithm'; 2 | 3 | describe('euclideanAlgorithm', () => { 4 | it('should calculate GCD', () => { 5 | expect(euclideanAlgorithm(0, 0)).toBe(0); 6 | expect(euclideanAlgorithm(2, 0)).toBe(2); 7 | expect(euclideanAlgorithm(0, 2)).toBe(2); 8 | expect(euclideanAlgorithm(1, 2)).toBe(1); 9 | expect(euclideanAlgorithm(2, 1)).toBe(1); 10 | expect(euclideanAlgorithm(6, 6)).toBe(6); 11 | expect(euclideanAlgorithm(2, 4)).toBe(2); 12 | expect(euclideanAlgorithm(4, 2)).toBe(2); 13 | expect(euclideanAlgorithm(12, 4)).toBe(4); 14 | expect(euclideanAlgorithm(4, 12)).toBe(4); 15 | expect(euclideanAlgorithm(5, 13)).toBe(1); 16 | expect(euclideanAlgorithm(27, 13)).toBe(1); 17 | expect(euclideanAlgorithm(24, 60)).toBe(12); 18 | expect(euclideanAlgorithm(60, 24)).toBe(12); 19 | expect(euclideanAlgorithm(252, 105)).toBe(21); 20 | expect(euclideanAlgorithm(105, 252)).toBe(21); 21 | expect(euclideanAlgorithm(1071, 462)).toBe(21); 22 | expect(euclideanAlgorithm(462, 1071)).toBe(21); 23 | expect(euclideanAlgorithm(462, -1071)).toBe(21); 24 | expect(euclideanAlgorithm(-462, -1071)).toBe(21); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/data-structures/graph/README.md: -------------------------------------------------------------------------------- 1 | # Graph 2 | 3 | In computer science, a graph is an abstract data type 4 | that is meant to implement the undirected graph and 5 | directed graph concepts from mathematics, specifically 6 | the field of graph theory 7 | 8 | A graph data structure consists of a finite (and possibly 9 | mutable) set of vertices or nodes or points, together 10 | with a set of unordered pairs of these vertices for an 11 | undirected graph or a set of ordered pairs for a 12 | directed graph. These pairs are known as edges, arcs, 13 | or lines for an undirected graph and as arrows, 14 | directed edges, directed arcs, or directed lines 15 | for a directed graph. The vertices may be part of 16 | the graph structure, or may be external entities 17 | represented by integer indices or references. 18 | 19 | ![Graph](https://www.tutorialspoint.com/data_structures_algorithms/images/graph.jpg) 20 | 21 | ## References 22 | 23 | - [Wikipedia](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)) 24 | - [Introduction to Graphs on YouTube](https://www.youtube.com/watch?v=gXgEDyodOJU&index=9&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 25 | - [Graphs representation on YouTube](https://www.youtube.com/watch?v=k1wraWzqtvQ&index=10&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 26 | -------------------------------------------------------------------------------- /src/data-structures/linked-list/README.md: -------------------------------------------------------------------------------- 1 | # Linked List 2 | 3 | In computer science, a linked list is a linear collection 4 | of data elements, in which linear order is not given by 5 | their physical placement in memory. Instead, each 6 | element points to the next. It is a data structure 7 | consisting of a group of nodes which together represent 8 | a sequence. Under the simplest form, each node is 9 | composed of data and a reference (in other words, 10 | a link) to the next node in the sequence. This structure 11 | allows for efficient insertion or removal of elements 12 | from any position in the sequence during iteration. 13 | More complex variants add additional links, allowing 14 | efficient insertion or removal from arbitrary element 15 | references. A drawback of linked lists is that access 16 | time is linear (and difficult to pipeline). Faster 17 | access, such as random access, is not feasible. Arrays 18 | have better cache locality as compared to linked lists. 19 | 20 | ![Linked List](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg) 21 | 22 | ## References 23 | 24 | - [Wikipedia](https://en.wikipedia.org/wiki/Linked_list) 25 | - [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 26 | -------------------------------------------------------------------------------- /src/algorithms/graph/bridges/README.md: -------------------------------------------------------------------------------- 1 | # Bridges in Graph 2 | 3 | In graph theory, a **bridge**, **isthmus**, **cut-edge**, or **cut arc** is an edge 4 | of a graph whose deletion increases its number of connected components. Equivalently, 5 | an edge is a bridge if and only if it is not contained in any cycle. A graph is said 6 | to be bridgeless or isthmus-free if it contains no bridges. 7 | 8 | ![Bridges in graph](https://upload.wikimedia.org/wikipedia/commons/d/df/Graph_cut_edges.svg) 9 | 10 | A graph with 16 vertices and 6 bridges (highlighted in red) 11 | 12 | ![Bridgeless](https://upload.wikimedia.org/wikipedia/commons/b/bf/Undirected.svg) 13 | 14 | An undirected connected graph with no cut edges 15 | 16 | ![Bridges in graph](https://www.geeksforgeeks.org/wp-content/uploads/Bridge1.png) 17 | 18 | ![Bridges in graph](https://www.geeksforgeeks.org/wp-content/uploads/Bridge2.png) 19 | 20 | ![Bridges in graph](https://www.geeksforgeeks.org/wp-content/uploads/Bridge3.png) 21 | 22 | ## References 23 | 24 | - [GeeksForGeeks on YouTube](https://www.youtube.com/watch?v=thLQYBlz2DM&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 25 | - [Wikipedia](https://en.wikipedia.org/wiki/Bridge_%28graph_theory%29#Tarjan.27s_Bridge-finding_algorithm) 26 | - [GeeksForGeeks](https://www.geeksforgeeks.org/bridge-in-a-graph/) 27 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/n-queens/QueenPosition.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Class that represents queen position on the chessboard. 3 | */ 4 | export default class QueenPosition { 5 | /** 6 | * @param {number} rowIndex 7 | * @param {number} columnIndex 8 | */ 9 | constructor(rowIndex, columnIndex) { 10 | this.rowIndex = rowIndex; 11 | this.columnIndex = columnIndex; 12 | } 13 | 14 | /** 15 | * @return {number} 16 | */ 17 | get leftDiagonal() { 18 | // Each position on the same left (\) diagonal has the same difference of 19 | // rowIndex and columnIndex. This fact may be used to quickly check if two 20 | // positions (queens) are on the same left diagonal. 21 | // @see https://youtu.be/xouin83ebxE?t=1m59s 22 | return this.rowIndex - this.columnIndex; 23 | } 24 | 25 | /** 26 | * @return {number} 27 | */ 28 | get rightDiagonal() { 29 | // Each position on the same right diagonal (/) has the same 30 | // sum of rowIndex and columnIndex. This fact may be used to quickly 31 | // check if two positions (queens) are on the same right diagonal. 32 | // @see https://youtu.be/xouin83ebxE?t=1m59s 33 | return this.rowIndex + this.columnIndex; 34 | } 35 | 36 | toString() { 37 | return `${this.rowIndex},${this.columnIndex}`; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/square-matrix-rotation/squareMatrixRotation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {*[][]} originalMatrix 3 | * @return {*[][]} 4 | */ 5 | export default function squareMatrixRotation(originalMatrix) { 6 | const matrix = originalMatrix.slice(); 7 | 8 | // Do top-right/bottom-left diagonal reflection of the matrix. 9 | for (let rowIndex = 0; rowIndex < matrix.length; rowIndex += 1) { 10 | for (let columnIndex = rowIndex + 1; columnIndex < matrix.length; columnIndex += 1) { 11 | // Swap elements. 12 | [ 13 | matrix[columnIndex][rowIndex], 14 | matrix[rowIndex][columnIndex], 15 | ] = [ 16 | matrix[rowIndex][columnIndex], 17 | matrix[columnIndex][rowIndex], 18 | ]; 19 | } 20 | } 21 | 22 | // Do horizontal reflection of the matrix. 23 | for (let rowIndex = 0; rowIndex < matrix.length; rowIndex += 1) { 24 | for (let columnIndex = 0; columnIndex < matrix.length / 2; columnIndex += 1) { 25 | // Swap elements. 26 | [ 27 | matrix[rowIndex][matrix.length - columnIndex - 1], 28 | matrix[rowIndex][columnIndex], 29 | ] = [ 30 | matrix[rowIndex][columnIndex], 31 | matrix[rowIndex][matrix.length - columnIndex - 1], 32 | ]; 33 | } 34 | } 35 | 36 | return matrix; 37 | } 38 | -------------------------------------------------------------------------------- /src/algorithms/string/levenshtein-distance/__test__/levenshteinDistance.test.js: -------------------------------------------------------------------------------- 1 | import levenshteinDistance from '../levenshteinDistance'; 2 | 3 | describe('levenshteinDistance', () => { 4 | it('should calculate edit distance between two strings', () => { 5 | expect(levenshteinDistance('', '')).toBe(0); 6 | expect(levenshteinDistance('a', '')).toBe(1); 7 | expect(levenshteinDistance('', 'a')).toBe(1); 8 | expect(levenshteinDistance('abc', '')).toBe(3); 9 | expect(levenshteinDistance('', 'abc')).toBe(3); 10 | 11 | // Should just add I to the beginning. 12 | expect(levenshteinDistance('islander', 'slander')).toBe(1); 13 | 14 | // Needs to substitute M by K, T by M and add an A to the end 15 | expect(levenshteinDistance('mart', 'karma')).toBe(3); 16 | 17 | // Substitute K by S, E by I and insert G at the end. 18 | expect(levenshteinDistance('kitten', 'sitting')).toBe(3); 19 | 20 | // Should add 4 letters FOOT at the beginning. 21 | expect(levenshteinDistance('ball', 'football')).toBe(4); 22 | 23 | // Should delete 4 letters FOOT at the beginning. 24 | expect(levenshteinDistance('football', 'foot')).toBe(4); 25 | 26 | // Needs to substitute the first 5 chars: INTEN by EXECU 27 | expect(levenshteinDistance('intention', 'execution')).toBe(5); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/algorithms/sets/power-set/powerSet.js: -------------------------------------------------------------------------------- 1 | export default function powerSet(originalSet) { 2 | const subSets = []; 3 | 4 | // We will have 2^n possible combinations (where n is a length of original set). 5 | // It is because for every element of original set we will decide whether to include 6 | // it or not (2 options for each set element). 7 | const numberOfCombinations = 2 ** originalSet.length; 8 | 9 | // Each number in binary representation in a range from 0 to 2^n does exactly what we need: 10 | // it shoes by its bits (0 or 1) whether to include related element from the set or not. 11 | // For example, for the set {1, 2, 3} the binary number of 010 would mean that we need to 12 | // include only "2" to the current set. 13 | for (let combinationIndex = 0; combinationIndex < numberOfCombinations; combinationIndex += 1) { 14 | const subSet = []; 15 | 16 | for (let setElementIndex = 0; setElementIndex < originalSet.length; setElementIndex += 1) { 17 | // Decide whether we need to include current element into the subset or not. 18 | if (combinationIndex & (1 << setElementIndex)) { 19 | subSet.push(originalSet[setElementIndex]); 20 | } 21 | } 22 | 23 | // Add current subset to the list of all subsets. 24 | subSets.push(subSet); 25 | } 26 | 27 | return subSets; 28 | } 29 | -------------------------------------------------------------------------------- /src/data-structures/trie/Trie.js: -------------------------------------------------------------------------------- 1 | import TrieNode from './TrieNode'; 2 | 3 | const HEAD_CHARACTER = '*'; 4 | 5 | export default class Trie { 6 | constructor() { 7 | this.head = new TrieNode(HEAD_CHARACTER); 8 | } 9 | 10 | addWord(word) { 11 | const characters = Array.from(word); 12 | let currentNode = this.head; 13 | for (let charIndex = 0; charIndex < characters.length; charIndex += 1) { 14 | const isComplete = charIndex === characters.length - 1; 15 | currentNode = currentNode.addChild(characters[charIndex], isComplete); 16 | } 17 | } 18 | 19 | suggestNextCharacters(word) { 20 | const lastCharacter = this.getLastCharacterNode(word); 21 | 22 | if (!lastCharacter) { 23 | return null; 24 | } 25 | 26 | return lastCharacter.suggestChildren(); 27 | } 28 | 29 | doesWordExist(word) { 30 | return !!this.getLastCharacterNode(word); 31 | } 32 | 33 | getLastCharacterNode(word) { 34 | const characters = Array.from(word); 35 | let currentNode = this.head; 36 | for (let charIndex = 0; charIndex < characters.length; charIndex += 1) { 37 | if (!currentNode.hasChild(characters[charIndex])) { 38 | return null; 39 | } 40 | currentNode = currentNode.getChild(characters[charIndex]); 41 | } 42 | 43 | return currentNode; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/algorithms/math/primality-test/README.md: -------------------------------------------------------------------------------- 1 | # Primality Test 2 | 3 | A **prime number** (or a **prime**) is a natural number greater than `1` that 4 | cannot be formed by multiplying two smaller natural numbers. A natural number 5 | greater than `1` that is not prime is called a composite number. For 6 | example, `5` is prime because the only ways of writing it as a 7 | product, `1 × 5` or `5 × 1`, involve `5` itself. However, `6` is 8 | composite because it is the product of two numbers `(2 × 3)` that are 9 | both smaller than `6`. 10 | 11 | ![Prime Numbers](https://upload.wikimedia.org/wikipedia/commons/f/f0/Primes-vs-composites.svg) 12 | 13 | A **primality test** is an algorithm for determining whether an input 14 | number is prime. Among other fields of mathematics, it is used 15 | for cryptography. Unlike integer factorization, primality tests 16 | do not generally give prime factors, only stating whether the 17 | input number is prime or not. Factorization is thought to be 18 | a computationally difficult problem, whereas primality testing 19 | is comparatively easy (its running time is polynomial in the 20 | size of the input). 21 | 22 | ## References 23 | 24 | - [Prime Numbers on Wikipedia](https://en.wikipedia.org/wiki/Prime_number) 25 | - [Primality Test on Wikipedia](https://en.wikipedia.org/wiki/Primality_test) 26 | -------------------------------------------------------------------------------- /src/algorithms/search/linear-search/__test__/linearSearch.test.js: -------------------------------------------------------------------------------- 1 | import linearSearch from '../linearSearch'; 2 | 3 | describe('linearSearch', () => { 4 | it('should search all numbers in array', () => { 5 | const array = [1, 2, 4, 6, 2]; 6 | 7 | expect(linearSearch(array, 10)).toEqual([]); 8 | expect(linearSearch(array, 1)).toEqual([0]); 9 | expect(linearSearch(array, 2)).toEqual([1, 4]); 10 | }); 11 | 12 | it('should search all strings in array', () => { 13 | const array = ['a', 'b', 'a']; 14 | 15 | expect(linearSearch(array, 'c')).toEqual([]); 16 | expect(linearSearch(array, 'b')).toEqual([1]); 17 | expect(linearSearch(array, 'a')).toEqual([0, 2]); 18 | }); 19 | 20 | it('should search through objects as well', () => { 21 | const comparatorCallback = (a, b) => { 22 | if (a.key === b.key) { 23 | return 0; 24 | } 25 | 26 | return a.key <= b.key ? -1 : 1; 27 | }; 28 | 29 | const array = [ 30 | { key: 5 }, 31 | { key: 6 }, 32 | { key: 7 }, 33 | { key: 6 }, 34 | ]; 35 | 36 | expect(linearSearch(array, { key: 10 }, comparatorCallback)).toEqual([]); 37 | expect(linearSearch(array, { key: 5 }, comparatorCallback)).toEqual([0]); 38 | expect(linearSearch(array, { key: 6 }, comparatorCallback)).toEqual([1, 3]); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/algorithms/sets/permutations/permutateWithoutRepetitions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {*[]} permutationOptions 3 | * @return {*[]} 4 | */ 5 | export default function permutateWithoutRepetitions(permutationOptions) { 6 | if (permutationOptions.length === 1) { 7 | return [permutationOptions]; 8 | } 9 | 10 | // Init permutations array. 11 | const permutations = []; 12 | 13 | // Get all permutations for permutationOptions excluding the first element. 14 | const smallerPermutations = permutateWithoutRepetitions(permutationOptions.slice(1)); 15 | 16 | // Insert first option into every possible position of every smaller permutation. 17 | const firstOption = permutationOptions[0]; 18 | 19 | for (let permIndex = 0; permIndex < smallerPermutations.length; permIndex += 1) { 20 | const smallerPermutation = smallerPermutations[permIndex]; 21 | 22 | // Insert first option into every possible position of smallerPermutation. 23 | for (let positionIndex = 0; positionIndex <= smallerPermutation.length; positionIndex += 1) { 24 | const permutationPrefix = smallerPermutation.slice(0, positionIndex); 25 | const permutationSuffix = smallerPermutation.slice(positionIndex); 26 | permutations.push(permutationPrefix.concat([firstOption], permutationSuffix)); 27 | } 28 | } 29 | 30 | return permutations; 31 | } 32 | -------------------------------------------------------------------------------- /src/algorithms/graph/travelling-salesman/README.md: -------------------------------------------------------------------------------- 1 | # Travelling Salesman Problem 2 | 3 | The travelling salesman problem (TSP) asks the following question: 4 | "Given a list of cities and the distances between each pair of 5 | cities, what is the shortest possible route that visits each city 6 | and returns to the origin city?" 7 | 8 | ![Travelling Salesman](https://upload.wikimedia.org/wikipedia/commons/1/11/GLPK_solution_of_a_travelling_salesman_problem.svg) 9 | 10 | Solution of a travelling salesman problem: the black line shows 11 | the shortest possible loop that connects every red dot. 12 | 13 | ![Travelling Salesman Graph](https://upload.wikimedia.org/wikipedia/commons/3/30/Weighted_K4.svg) 14 | 15 | TSP can be modelled as an undirected weighted graph, such that 16 | cities are the graph's vertices, paths are the graph's edges, 17 | and a path's distance is the edge's weight. It is a minimization 18 | problem starting and finishing at a specified vertex after having 19 | visited each other vertex exactly once. Often, the model is a 20 | complete graph (i.e. each pair of vertices is connected by an 21 | edge). If no path exists between two cities, adding an arbitrarily 22 | long edge will complete the graph without affecting the optimal tour. 23 | 24 | ## References 25 | 26 | - [Wikipedia](https://en.wikipedia.org/wiki/Travelling_salesman_problem) 27 | -------------------------------------------------------------------------------- /src/algorithms/math/primality-test/__test__/trialDivision.test.js: -------------------------------------------------------------------------------- 1 | import trialDivision from '../trialDivision'; 2 | 3 | /** 4 | * @param {function(n: number)} testFunction 5 | */ 6 | function primalityTest(testFunction) { 7 | expect(testFunction(1)).toBeFalsy(); 8 | expect(testFunction(2)).toBeTruthy(); 9 | expect(testFunction(3)).toBeTruthy(); 10 | expect(testFunction(5)).toBeTruthy(); 11 | expect(testFunction(11)).toBeTruthy(); 12 | expect(testFunction(191)).toBeTruthy(); 13 | expect(testFunction(191)).toBeTruthy(); 14 | expect(testFunction(199)).toBeTruthy(); 15 | 16 | expect(testFunction(-1)).toBeFalsy(); 17 | expect(testFunction(0)).toBeFalsy(); 18 | expect(testFunction(4)).toBeFalsy(); 19 | expect(testFunction(6)).toBeFalsy(); 20 | expect(testFunction(12)).toBeFalsy(); 21 | expect(testFunction(14)).toBeFalsy(); 22 | expect(testFunction(25)).toBeFalsy(); 23 | expect(testFunction(192)).toBeFalsy(); 24 | expect(testFunction(200)).toBeFalsy(); 25 | expect(testFunction(400)).toBeFalsy(); 26 | 27 | // It should also deal with floats. 28 | expect(testFunction(0.5)).toBeFalsy(); 29 | expect(testFunction(1.3)).toBeFalsy(); 30 | expect(testFunction(10.5)).toBeFalsy(); 31 | } 32 | 33 | describe('trialDivision', () => { 34 | it('should detect prime numbers', () => { 35 | primalityTest(trialDivision); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/data-structures/queue/README.md: -------------------------------------------------------------------------------- 1 | # Queue 2 | 3 | In computer science, a queue is a particular kind of abstract data 4 | type or collection in which the entities in the collection are 5 | kept in order and the principle (or only) operations on the 6 | collection are the addition of entities to the rear terminal 7 | position, known as enqueue, and removal of entities from the 8 | front terminal position, known as dequeue. This makes the queue 9 | a First-In-First-Out (FIFO) data structure. In a FIFO data 10 | structure, the first element added to the queue will be the 11 | first one to be removed. This is equivalent to the requirement 12 | that once a new element is added, all elements that were added 13 | before have to be removed before the new element can be removed. 14 | Often a peek or front operation is also entered, returning the 15 | value of the front element without dequeuing it. A queue is an 16 | example of a linear data structure, or more abstractly a 17 | sequential collection. 18 | 19 | Representation of a FIFO (first in, first out) queue 20 | 21 | ![Queue](https://upload.wikimedia.org/wikipedia/commons/5/52/Data_Queue.svg) 22 | 23 | ## References 24 | 25 | - [Wikipedia](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) 26 | - [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&) 27 | -------------------------------------------------------------------------------- /src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js: -------------------------------------------------------------------------------- 1 | import DisjointSet from '../../../data-structures/disjoint-set/DisjointSet'; 2 | 3 | /** 4 | * Detect cycle in undirected graph using disjoint sets. 5 | * 6 | * @param {Graph} graph 7 | */ 8 | export default function detectUndirectedCycleUsingDisjointSet(graph) { 9 | // Create initial singleton disjoint sets for each graph vertex. 10 | /** @param {GraphVertex} graphVertex */ 11 | const keyExtractor = graphVertex => graphVertex.getKey(); 12 | const disjointSet = new DisjointSet(keyExtractor); 13 | graph.getAllVertices().forEach(graphVertex => disjointSet.makeSet(graphVertex)); 14 | 15 | // Go trough all graph edges one by one and check if edge vertices are from the 16 | // different sets. In this case joint those sets together. Do this until you find 17 | // an edge where to edge vertices are already in one set. This means that current 18 | // edge will create a cycle. 19 | let cycleFound = false; 20 | /** @param {GraphEdge} graphEdge */ 21 | graph.getAllEdges().forEach((graphEdge) => { 22 | if (disjointSet.inSameSet(graphEdge.startVertex, graphEdge.endVertex)) { 23 | // Cycle found. 24 | cycleFound = true; 25 | } else { 26 | disjointSet.union(graphEdge.startVertex, graphEdge.endVertex); 27 | } 28 | }); 29 | 30 | return cycleFound; 31 | } 32 | -------------------------------------------------------------------------------- /src/data-structures/tree/README.md: -------------------------------------------------------------------------------- 1 | # Tree 2 | 3 | * [Binary Search Tree](binary-search-tree) 4 | * [AVL Tree](avl-tree) 5 | * [Red-Black Tree](red-black-tree) 6 | * [Segment Tree](segment-tree) - with min/max/sum range queries examples 7 | * [Fenwick Tree](fenwick-tree) (Binary Indexed Tree) 8 | 9 | In computer science, a tree is a widely used abstract data 10 | type (ADT) — or data structure implementing this ADT—that 11 | simulates a hierarchical tree structure, with a root value 12 | and subtrees of children with a parent node, represented as 13 | a set of linked nodes. 14 | 15 | A tree data structure can be defined recursively (locally) 16 | as a collection of nodes (starting at a root node), where 17 | each node is a data structure consisting of a value, 18 | together with a list of references to nodes (the "children"), 19 | with the constraints that no reference is duplicated, and none 20 | points to the root. 21 | 22 | A simple unordered tree; in this diagram, the node labeled 7 has 23 | two children, labeled 2 and 6, and one parent, labeled 2. The 24 | root node, at the top, has no parent. 25 | 26 | ![Tree](https://upload.wikimedia.org/wikipedia/commons/f/f7/Binary_tree.svg) 27 | 28 | ## References 29 | 30 | - [Wikipedia](https://en.wikipedia.org/wiki/Tree_(data_structure)) 31 | - [YouTube](https://www.youtube.com/watch?v=oSWTXtMglKE&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=8) 32 | -------------------------------------------------------------------------------- /src/algorithms/sorting/shell-sort/ShellSort.js: -------------------------------------------------------------------------------- 1 | import Sort from '../Sort'; 2 | 3 | export default class ShellSort extends Sort { 4 | sort(originalArray) { 5 | // Prevent original array from mutations. 6 | const array = [...originalArray]; 7 | 8 | // Define a gap distance. 9 | let gap = Math.floor(array.length / 2); 10 | 11 | // Until gap is bigger then zero do elements comparisons and swaps. 12 | while (gap > 0) { 13 | // Go and compare all distant element pairs. 14 | for (let i = 0; i < (array.length - gap); i += 1) { 15 | let currentIndex = i; 16 | let gapShiftedIndex = i + gap; 17 | 18 | while (currentIndex >= 0) { 19 | // Call visiting callback. 20 | this.callbacks.visitingCallback(array[currentIndex]); 21 | 22 | // Compare and swap array elements if needed. 23 | if (this.comparator.lessThan(array[gapShiftedIndex], array[currentIndex])) { 24 | const tmp = array[currentIndex]; 25 | array[currentIndex] = array[gapShiftedIndex]; 26 | array[gapShiftedIndex] = tmp; 27 | } 28 | 29 | gapShiftedIndex = currentIndex; 30 | currentIndex -= gap; 31 | } 32 | } 33 | 34 | // Shrink the gap. 35 | gap = Math.floor(gap / 2); 36 | } 37 | 38 | // Return sorted copy of an original array. 39 | return array; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/algorithms/search/jump-search/README.md: -------------------------------------------------------------------------------- 1 | # Jump Search 2 | 3 | Like Binary Search, **Jump Search** (or **Block Search**) is a searching algorithm 4 | for sorted arrays. The basic idea is to check fewer elements (than linear search) 5 | by jumping ahead by fixed steps or skipping some elements in place of searching all 6 | elements. 7 | 8 | For example, suppose we have an array `arr[]` of size `n` and block (to be jumped) 9 | of size `m`. Then we search at the indexes `arr[0]`, `arr[m]`, `arr[2 * m]`, ..., `arr[k * m]` and 10 | so on. Once we find the interval `arr[k * m] < x < arr[(k+1) * m]`, we perform a 11 | linear search operation from the index `k * m` to find the element `x`. 12 | 13 | **What is the optimal block size to be skipped?** 14 | In the worst case, we have to do `n/m` jumps and if the last checked value is 15 | greater than the element to be searched for, we perform `m - 1` comparisons more 16 | for linear search. Therefore the total number of comparisons in the worst case 17 | will be `((n/m) + m - 1)`. The value of the function `((n/m) + m - 1)` will be 18 | minimum when `m = √n`. Therefore, the best step size is `m = √n`. 19 | 20 | ## Complexity 21 | 22 | **Time complexity**: `O(√n)` - because we do search by blocks of size `√n`. 23 | 24 | ## References 25 | 26 | - [GeeksForGeeks](https://www.geeksforgeeks.org/jump-search/) 27 | - [Wikipedia](https://en.wikipedia.org/wiki/Jump_search) 28 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/hanoi-tower/README.md: -------------------------------------------------------------------------------- 1 | # Tower of Hanoi 2 | 3 | The Tower of Hanoi (also called the Tower of Brahma or Lucas' 4 | Tower and sometimes pluralized) is a mathematical game or puzzle. 5 | It consists of three rods and a number of disks of different sizes, 6 | which can slide onto any rod. The puzzle starts with the disks in 7 | a neat stack in ascending order of size on one rod, the smallest 8 | at the top, thus making a conical shape. 9 | 10 | The objective of the puzzle is to move the entire stack to another 11 | rod, obeying the following simple rules: 12 | 13 | - Only one disk can be moved at a time. 14 | - Each move consists of taking the upper disk from one of the 15 | stacks and placing it on top of another stack or on an empty rod. 16 | - No disk may be placed on top of a smaller disk. 17 | 18 | ![Hanoi Tower](https://upload.wikimedia.org/wikipedia/commons/8/8d/Iterative_algorithm_solving_a_6_disks_Tower_of_Hanoi.gif) 19 | 20 | Animation of an iterative algorithm solving 6-disk problem 21 | 22 | With `3` disks, the puzzle can be solved in `7` moves. The minimal 23 | number of moves required to solve a Tower of Hanoi puzzle 24 | is `2^n − 1`, where `n` is the number of disks. 25 | 26 | ## References 27 | 28 | - [Wikipedia](https://en.wikipedia.org/wiki/Tower_of_Hanoi) 29 | - [HackerEarth](https://www.hackerearth.com/blog/algorithms/tower-hanoi-recursion-game-algorithm-explained/) 30 | -------------------------------------------------------------------------------- /src/algorithms/sets/knapsack-problem/__test__/KnapsackItem.test.js: -------------------------------------------------------------------------------- 1 | import KnapsackItem from '../KnapsackItem'; 2 | 3 | describe('KnapsackItem', () => { 4 | it('should create knapsack item and count its total weight and value', () => { 5 | const knapsackItem = new KnapsackItem({ value: 3, weight: 2 }); 6 | 7 | expect(knapsackItem.value).toBe(3); 8 | expect(knapsackItem.weight).toBe(2); 9 | expect(knapsackItem.quantity).toBe(1); 10 | expect(knapsackItem.valuePerWeightRatio).toBe(1.5); 11 | expect(knapsackItem.toString()).toBe('v3 w2 x 1'); 12 | expect(knapsackItem.totalValue).toBe(3); 13 | expect(knapsackItem.totalWeight).toBe(2); 14 | 15 | knapsackItem.quantity = 0; 16 | 17 | expect(knapsackItem.value).toBe(3); 18 | expect(knapsackItem.weight).toBe(2); 19 | expect(knapsackItem.quantity).toBe(0); 20 | expect(knapsackItem.valuePerWeightRatio).toBe(1.5); 21 | expect(knapsackItem.toString()).toBe('v3 w2 x 0'); 22 | expect(knapsackItem.totalValue).toBe(0); 23 | expect(knapsackItem.totalWeight).toBe(0); 24 | 25 | knapsackItem.quantity = 2; 26 | 27 | expect(knapsackItem.value).toBe(3); 28 | expect(knapsackItem.weight).toBe(2); 29 | expect(knapsackItem.quantity).toBe(2); 30 | expect(knapsackItem.valuePerWeightRatio).toBe(1.5); 31 | expect(knapsackItem.toString()).toBe('v3 w2 x 2'); 32 | expect(knapsackItem.totalValue).toBe(6); 33 | expect(knapsackItem.totalWeight).toBe(4); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/algorithms/math/is-power-of-two/README.md: -------------------------------------------------------------------------------- 1 | # Is a power of two 2 | 3 | Given a positive integer, write a function to find if it is 4 | a power of two or not. 5 | 6 | **Naive solution** 7 | 8 | In naive solution we just keep dividing the number by two 9 | unless the number becomes `1` and every time we do so we 10 | check that remainder after division is always `0`. Otherwise 11 | the number can't be a power of two. 12 | 13 | **Bitwise solution** 14 | 15 | Powers of two in binary form always have just one bit. 16 | The only exception is with a signed integer (e.g. an 8-bit 17 | signed integer with a value of -128 looks like: `10000000`) 18 | 19 | ``` 20 | 1: 0001 21 | 2: 0010 22 | 4: 0100 23 | 8: 1000 24 | ``` 25 | 26 | So after checking that the number is greater than zero, 27 | we can use a bitwise hack to test that one and only one 28 | bit is set. 29 | 30 | ``` 31 | number & (number - 1) 32 | ``` 33 | 34 | For example for number `8` that operations will look like: 35 | 36 | ``` 37 | 1000 38 | - 0001 39 | ---- 40 | 0111 41 | 42 | 1000 43 | & 0111 44 | ---- 45 | 0000 46 | ``` 47 | 48 | ## References 49 | 50 | - [GeeksForGeeks](https://www.geeksforgeeks.org/program-to-find-whether-a-no-is-power-of-two/) 51 | - [Bitwise Solution on Stanford](http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2) 52 | - [Binary number subtraction on YouTube](https://www.youtube.com/watch?v=S9LJknZTyos&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=66) 53 | -------------------------------------------------------------------------------- /src/algorithms/string/levenshtein-distance/levenshteinDistance.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} a 3 | * @param {string} b 4 | * @return {number} 5 | */ 6 | export default function levenshteinDistance(a, b) { 7 | // Create empty edit distance matrix for all possible modifications of 8 | // substrings of a to substrings of b. 9 | const distanceMatrix = Array(b.length + 1).fill(null).map(() => Array(a.length + 1).fill(null)); 10 | 11 | // Fill the first row of the matrix. 12 | // If this is first row then we're transforming empty string to a. 13 | // In this case the number of transformations equals to size of a substring. 14 | for (let i = 0; i <= a.length; i += 1) { 15 | distanceMatrix[0][i] = i; 16 | } 17 | 18 | // Fill the first column of the matrix. 19 | // If this is first column then we're transforming empty string to b. 20 | // In this case the number of transformations equals to size of b substring. 21 | for (let j = 0; j <= b.length; j += 1) { 22 | distanceMatrix[j][0] = j; 23 | } 24 | 25 | for (let j = 1; j <= b.length; j += 1) { 26 | for (let i = 1; i <= a.length; i += 1) { 27 | const indicator = a[i - 1] === b[j - 1] ? 0 : 1; 28 | distanceMatrix[j][i] = Math.min( 29 | distanceMatrix[j][i - 1] + 1, // deletion 30 | distanceMatrix[j - 1][i] + 1, // insertion 31 | distanceMatrix[j - 1][i - 1] + indicator, // substitution 32 | ); 33 | } 34 | } 35 | 36 | return distanceMatrix[b.length][a.length]; 37 | } 38 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/n-queens/__test__/nQueens.test.js: -------------------------------------------------------------------------------- 1 | import nQueens from '../nQueens'; 2 | 3 | describe('nQueens', () => { 4 | it('should not hae solution for 3 queens', () => { 5 | const solutions = nQueens(3); 6 | expect(solutions.length).toBe(0); 7 | }); 8 | 9 | it('should solve n-queens problem for 4 queens', () => { 10 | const solutions = nQueens(4); 11 | expect(solutions.length).toBe(2); 12 | 13 | // First solution. 14 | expect(solutions[0][0].toString()).toBe('0,1'); 15 | expect(solutions[0][1].toString()).toBe('1,3'); 16 | expect(solutions[0][2].toString()).toBe('2,0'); 17 | expect(solutions[0][3].toString()).toBe('3,2'); 18 | 19 | // Second solution (mirrored). 20 | expect(solutions[1][0].toString()).toBe('0,2'); 21 | expect(solutions[1][1].toString()).toBe('1,0'); 22 | expect(solutions[1][2].toString()).toBe('2,3'); 23 | expect(solutions[1][3].toString()).toBe('3,1'); 24 | }); 25 | 26 | it('should solve n-queens problem for 6 queens', () => { 27 | const solutions = nQueens(6); 28 | expect(solutions.length).toBe(4); 29 | 30 | // First solution. 31 | expect(solutions[0][0].toString()).toBe('0,1'); 32 | expect(solutions[0][1].toString()).toBe('1,3'); 33 | expect(solutions[0][2].toString()).toBe('2,5'); 34 | expect(solutions[0][3].toString()).toBe('3,0'); 35 | expect(solutions[0][4].toString()).toBe('4,2'); 36 | expect(solutions[0][5].toString()).toBe('5,4'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/algorithms/sets/longest-increasing-subsequence/README.md: -------------------------------------------------------------------------------- 1 | # Longest Increasing Subsequence 2 | 3 | The longest increasing subsequence problem is to find a subsequence of a 4 | given sequence in which the subsequence's elements are in sorted order, 5 | lowest to highest, and in which the subsequence is as long as possible. 6 | This subsequence is not necessarily contiguous, or unique. 7 | 8 | ## Complexity 9 | 10 | The longest increasing subsequence problem is solvable in 11 | time `O(n log n)`, where `n` denotes the length of the input sequence. 12 | 13 | Dynamic programming approach has complexity `O(n * n)`. 14 | 15 | ## Example 16 | 17 | In the first 16 terms of the binary Van der Corput sequence 18 | 19 | ``` 20 | 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 21 | ``` 22 | 23 | a longest increasing subsequence is 24 | 25 | ``` 26 | 0, 2, 6, 9, 11, 15. 27 | ``` 28 | 29 | This subsequence has length six; 30 | the input sequence has no seven-member increasing subsequences. 31 | The longest increasing subsequence in this example is not unique: for 32 | instance, 33 | 34 | ``` 35 | 0, 4, 6, 9, 11, 15 or 36 | 0, 2, 6, 9, 13, 15 or 37 | 0, 4, 6, 9, 13, 15 38 | ``` 39 | 40 | are other increasing subsequences of equal length in the same 41 | input sequence. 42 | 43 | ## References 44 | 45 | - [Wikipedia](https://en.wikipedia.org/wiki/Longest_increasing_subsequence) 46 | - [Dynamic Programming Approach on YouTube](https://www.youtube.com/watch?v=CE2b_-XfVDk&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 47 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/square-matrix-rotation/__test__/squareMatrixRotation.test.js: -------------------------------------------------------------------------------- 1 | import squareMatrixRotation from '../squareMatrixRotation'; 2 | 3 | describe('squareMatrixRotation', () => { 4 | it('should rotate matrix #0 in-place', () => { 5 | const matrix = [[1]]; 6 | 7 | const rotatedMatrix = [[1]]; 8 | 9 | expect(squareMatrixRotation(matrix)).toEqual(rotatedMatrix); 10 | }); 11 | 12 | it('should rotate matrix #1 in-place', () => { 13 | const matrix = [ 14 | [1, 2], 15 | [3, 4], 16 | ]; 17 | 18 | const rotatedMatrix = [ 19 | [3, 1], 20 | [4, 2], 21 | ]; 22 | 23 | expect(squareMatrixRotation(matrix)).toEqual(rotatedMatrix); 24 | }); 25 | 26 | it('should rotate matrix #2 in-place', () => { 27 | const matrix = [ 28 | [1, 2, 3], 29 | [4, 5, 6], 30 | [7, 8, 9], 31 | ]; 32 | 33 | const rotatedMatrix = [ 34 | [7, 4, 1], 35 | [8, 5, 2], 36 | [9, 6, 3], 37 | ]; 38 | 39 | expect(squareMatrixRotation(matrix)).toEqual(rotatedMatrix); 40 | }); 41 | 42 | it('should rotate matrix #3 in-place', () => { 43 | const matrix = [ 44 | [5, 1, 9, 11], 45 | [2, 4, 8, 10], 46 | [13, 3, 6, 7], 47 | [15, 14, 12, 16], 48 | ]; 49 | 50 | const rotatedMatrix = [ 51 | [15, 13, 2, 5], 52 | [14, 3, 4, 1], 53 | [12, 6, 8, 9], 54 | [16, 7, 10, 11], 55 | ]; 56 | 57 | expect(squareMatrixRotation(matrix)).toEqual(rotatedMatrix); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /src/algorithms/math/sieve-of-eratosthenes/README.md: -------------------------------------------------------------------------------- 1 | # Sieve of Eratosthenes 2 | 3 | The Sieve of Eratosthenes is an algorithm for finding all prime numbers up to some limit `n`. 4 | 5 | It is attributed to Eratosthenes of Cyrene, an ancient Greek mathematician. 6 | 7 | ## How it works 8 | 9 | 1. Create a boolean array of `n + 1` positions (to represent the numbers `0` through `n`) 10 | 2. Set positions `0` and `1` to `false`, and the rest to `true` 11 | 3. Start at position `p = 2` (the first prime number) 12 | 4. Mark as `false` all the multiples of `p` (that is, positions `2 * p`, `3 * p`, `4 * p`... until you reach the end of the array) 13 | 5. Find the first position greater than `p` that is `true` in the array. If there is no such position, stop. Otherwise, let `p` equal this new number (which is the next prime), and repeat from step 4 14 | 15 | When the algorithm terminates, the numbers remaining `true` in the array are all 16 | the primes below `n`. 17 | 18 | An improvement of this algorithm is, in step 4, start marking multiples 19 | of `p` from `p * p`, and not from `2 * p`. The reason why this works is because, 20 | at that point, smaller multiples of `p` will have already been marked `false`. 21 | 22 | ## Example 23 | 24 | ![Sieve](https://upload.wikimedia.org/wikipedia/commons/b/b9/Sieve_of_Eratosthenes_animation.gif) 25 | 26 | ## Complexity 27 | 28 | The algorithm has a complexity of `O(n log(log n))`. 29 | 30 | ## References 31 | 32 | - [Wikipedia](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) 33 | -------------------------------------------------------------------------------- /src/algorithms/sets/shortest-common-supersequence/__test__/shortestCommonSupersequence.test.js: -------------------------------------------------------------------------------- 1 | import shortestCommonSupersequence from '../shortestCommonSupersequence'; 2 | 3 | describe('shortestCommonSupersequence', () => { 4 | it('should find shortest common supersequence of two sequences', () => { 5 | // LCS (longest common subsequence) is empty 6 | expect(shortestCommonSupersequence( 7 | ['A', 'B', 'C'], 8 | ['D', 'E', 'F'], 9 | )).toEqual(['A', 'B', 'C', 'D', 'E', 'F']); 10 | 11 | // LCS (longest common subsequence) is "EE" 12 | expect(shortestCommonSupersequence( 13 | ['G', 'E', 'E', 'K'], 14 | ['E', 'K', 'E'], 15 | )).toEqual(['G', 'E', 'K', 'E', 'K']); 16 | 17 | // LCS (longest common subsequence) is "GTAB" 18 | expect(shortestCommonSupersequence( 19 | ['A', 'G', 'G', 'T', 'A', 'B'], 20 | ['G', 'X', 'T', 'X', 'A', 'Y', 'B'], 21 | )).toEqual(['A', 'G', 'G', 'X', 'T', 'X', 'A', 'Y', 'B']); 22 | 23 | // LCS (longest common subsequence) is "BCBA". 24 | expect(shortestCommonSupersequence( 25 | ['A', 'B', 'C', 'B', 'D', 'A', 'B'], 26 | ['B', 'D', 'C', 'A', 'B', 'A'], 27 | )).toEqual(['A', 'B', 'D', 'C', 'A', 'B', 'D', 'A', 'B']); 28 | 29 | // LCS (longest common subsequence) is "BDABA". 30 | expect(shortestCommonSupersequence( 31 | ['B', 'D', 'C', 'A', 'B', 'A'], 32 | ['A', 'B', 'C', 'B', 'D', 'A', 'B', 'A', 'C'], 33 | )).toEqual(['A', 'B', 'C', 'B', 'D', 'C', 'A', 'B', 'A', 'C']); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/algorithms/graph/detect-cycle/__test__/detectUndirectedCycleUsingDisjointSet.test.js: -------------------------------------------------------------------------------- 1 | import GraphVertex from '../../../../data-structures/graph/GraphVertex'; 2 | import GraphEdge from '../../../../data-structures/graph/GraphEdge'; 3 | import Graph from '../../../../data-structures/graph/Graph'; 4 | import detectUndirectedCycleUsingDisjointSet from '../detectUndirectedCycleUsingDisjointSet'; 5 | 6 | describe('detectUndirectedCycleUsingDisjointSet', () => { 7 | it('should detect undirected cycle', () => { 8 | const vertexA = new GraphVertex('A'); 9 | const vertexB = new GraphVertex('B'); 10 | const vertexC = new GraphVertex('C'); 11 | const vertexD = new GraphVertex('D'); 12 | const vertexE = new GraphVertex('E'); 13 | const vertexF = new GraphVertex('F'); 14 | 15 | const edgeAF = new GraphEdge(vertexA, vertexF); 16 | const edgeAB = new GraphEdge(vertexA, vertexB); 17 | const edgeBE = new GraphEdge(vertexB, vertexE); 18 | const edgeBC = new GraphEdge(vertexB, vertexC); 19 | const edgeCD = new GraphEdge(vertexC, vertexD); 20 | const edgeDE = new GraphEdge(vertexD, vertexE); 21 | 22 | const graph = new Graph(); 23 | graph 24 | .addEdge(edgeAF) 25 | .addEdge(edgeAB) 26 | .addEdge(edgeBE) 27 | .addEdge(edgeBC) 28 | .addEdge(edgeCD); 29 | 30 | expect(detectUndirectedCycleUsingDisjointSet(graph)).toBeFalsy(); 31 | 32 | graph.addEdge(edgeDE); 33 | 34 | expect(detectUndirectedCycleUsingDisjointSet(graph)).toBeTruthy(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/algorithms/graph/detect-cycle/__test__/detectUndirectedCycle.test.js: -------------------------------------------------------------------------------- 1 | import GraphVertex from '../../../../data-structures/graph/GraphVertex'; 2 | import GraphEdge from '../../../../data-structures/graph/GraphEdge'; 3 | import Graph from '../../../../data-structures/graph/Graph'; 4 | import detectUndirectedCycle from '../detectUndirectedCycle'; 5 | 6 | describe('detectUndirectedCycle', () => { 7 | it('should detect undirected cycle', () => { 8 | const vertexA = new GraphVertex('A'); 9 | const vertexB = new GraphVertex('B'); 10 | const vertexC = new GraphVertex('C'); 11 | const vertexD = new GraphVertex('D'); 12 | const vertexE = new GraphVertex('E'); 13 | const vertexF = new GraphVertex('F'); 14 | 15 | const edgeAF = new GraphEdge(vertexA, vertexF); 16 | const edgeAB = new GraphEdge(vertexA, vertexB); 17 | const edgeBE = new GraphEdge(vertexB, vertexE); 18 | const edgeBC = new GraphEdge(vertexB, vertexC); 19 | const edgeCD = new GraphEdge(vertexC, vertexD); 20 | const edgeDE = new GraphEdge(vertexD, vertexE); 21 | 22 | const graph = new Graph(); 23 | graph 24 | .addEdge(edgeAF) 25 | .addEdge(edgeAB) 26 | .addEdge(edgeBE) 27 | .addEdge(edgeBC) 28 | .addEdge(edgeCD); 29 | 30 | expect(detectUndirectedCycle(graph)).toBeNull(); 31 | 32 | graph.addEdge(edgeDE); 33 | 34 | expect(detectUndirectedCycle(graph)).toEqual({ 35 | B: vertexC, 36 | C: vertexD, 37 | D: vertexE, 38 | E: vertexB, 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/knight-tour/README.md: -------------------------------------------------------------------------------- 1 | # Knight's Tour 2 | 3 | A **knight's tour** is a sequence of moves of a knight on a chessboard 4 | such that the knight visits every square only once. If the knight 5 | ends on a square that is one knight's move from the beginning 6 | square (so that it could tour the board again immediately, 7 | following the same path), the tour is **closed**, otherwise it 8 | is **open**. 9 | 10 | The **knight's tour problem** is the mathematical problem of 11 | finding a knight's tour. Creating a program to find a knight's 12 | tour is a common problem given to computer science students. 13 | Variations of the knight's tour problem involve chessboards of 14 | different sizes than the usual `8×8`, as well as irregular 15 | (non-rectangular) boards. 16 | 17 | The knight's tour problem is an instance of the more 18 | general **Hamiltonian path problem** in graph theory. The problem of finding 19 | a closed knight's tour is similarly an instance of the Hamiltonian 20 | cycle problem. 21 | 22 | ![Knight's Tour](https://upload.wikimedia.org/wikipedia/commons/d/da/Knight%27s_tour_anim_2.gif) 23 | 24 | An open knight's tour of a chessboard. 25 | 26 | ![Knight's Tour](https://upload.wikimedia.org/wikipedia/commons/c/ca/Knights-Tour-Animation.gif) 27 | 28 | An animation of an open knight's tour on a 5 by 5 board. 29 | 30 | ## References 31 | 32 | - [Wikipedia](https://en.wikipedia.org/wiki/Knight%27s_tour) 33 | - [GeeksForGeeks](https://www.geeksforgeeks.org/backtracking-set-1-the-knights-tour-problem/) 34 | -------------------------------------------------------------------------------- /src/algorithms/search/binary-search/__test__/binarySearch.test.js: -------------------------------------------------------------------------------- 1 | import binarySearch from '../binarySearch'; 2 | 3 | describe('binarySearch', () => { 4 | it('should search number in sorted array', () => { 5 | expect(binarySearch([], 1)).toBe(-1); 6 | expect(binarySearch([1], 1)).toBe(0); 7 | expect(binarySearch([1, 2], 1)).toBe(0); 8 | expect(binarySearch([1, 2], 2)).toBe(1); 9 | expect(binarySearch([1, 5, 10, 12], 1)).toBe(0); 10 | expect(binarySearch([1, 5, 10, 12, 14, 17, 22, 100], 17)).toBe(5); 11 | expect(binarySearch([1, 5, 10, 12, 14, 17, 22, 100], 1)).toBe(0); 12 | expect(binarySearch([1, 5, 10, 12, 14, 17, 22, 100], 100)).toBe(7); 13 | expect(binarySearch([1, 5, 10, 12, 14, 17, 22, 100], 0)).toBe(-1); 14 | }); 15 | 16 | it('should search object in sorted array', () => { 17 | const sortedArrayOfObjects = [ 18 | { key: 1, value: 'value1' }, 19 | { key: 2, value: 'value2' }, 20 | { key: 3, value: 'value3' }, 21 | ]; 22 | 23 | const comparator = (a, b) => { 24 | if (a.key === b.key) return 0; 25 | return a.key < b.key ? -1 : 1; 26 | }; 27 | 28 | expect(binarySearch([], { key: 1 }, comparator)).toBe(-1); 29 | expect(binarySearch(sortedArrayOfObjects, { key: 4 }, comparator)).toBe(-1); 30 | expect(binarySearch(sortedArrayOfObjects, { key: 1 }, comparator)).toBe(0); 31 | expect(binarySearch(sortedArrayOfObjects, { key: 2 }, comparator)).toBe(1); 32 | expect(binarySearch(sortedArrayOfObjects, { key: 3 }, comparator)).toBe(2); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/algorithms/sets/combination-sum/README.md: -------------------------------------------------------------------------------- 1 | # Combination Sum Problem 2 | 3 | Given a **set** of candidate numbers (`candidates`) **(without duplicates)** and 4 | a target number (`target`), find all unique combinations in `candidates` where 5 | the candidate numbers sums to `target`. 6 | 7 | The **same** repeated number may be chosen from `candidates` unlimited number 8 | of times. 9 | 10 | **Note:** 11 | 12 | - All numbers (including `target`) will be positive integers. 13 | - The solution set must not contain duplicate combinations. 14 | 15 | ## Examples 16 | 17 | ``` 18 | Input: candidates = [2,3,6,7], target = 7, 19 | 20 | A solution set is: 21 | [ 22 | [7], 23 | [2,2,3] 24 | ] 25 | ``` 26 | 27 | ``` 28 | Input: candidates = [2,3,5], target = 8, 29 | 30 | A solution set is: 31 | [ 32 | [2,2,2,2], 33 | [2,3,3], 34 | [3,5] 35 | ] 36 | ``` 37 | 38 | ## Explanations 39 | 40 | Since the problem is to get all the possible results, not the best or the 41 | number of result, thus we don’t need to consider DP (dynamic programming), 42 | backtracking approach using recursion is needed to handle it. 43 | 44 | Here is an example of decision tree for the situation when `candidates = [2, 3]` and `target = 6`: 45 | 46 | ``` 47 | 0 48 | / \ 49 | +2 +3 50 | / \ \ 51 | +2 +3 +3 52 | / \ / \ \ 53 | +2 ✘ ✘ ✘ ✓ 54 | / \ 55 | ✓ ✘ 56 | ``` 57 | 58 | ## References 59 | 60 | - [LeetCode](https://leetcode.com/problems/combination-sum/description/) 61 | -------------------------------------------------------------------------------- /src/data-structures/doubly-linked-list/README.md: -------------------------------------------------------------------------------- 1 | # Doubly Linked List 2 | 3 | In computer science, a **doubly linked list** is a linked data structure that 4 | consists of a set of sequentially linked records called nodes. Each node contains 5 | two fields, called links, that are references to the previous and to the next 6 | node in the sequence of nodes. The beginning and ending nodes' previous and next 7 | links, respectively, point to some kind of terminator, typically a sentinel 8 | node or null, to facilitate traversal of the list. If there is only one 9 | sentinel node, then the list is circularly linked via the sentinel node. It can 10 | be conceptualized as two singly linked lists formed from the same data items, 11 | but in opposite sequential orders. 12 | 13 | ![Doubly Linked List](https://upload.wikimedia.org/wikipedia/commons/5/5e/Doubly-linked-list.svg) 14 | 15 | The two node links allow traversal of the list in either direction. While adding 16 | or removing a node in a doubly linked list requires changing more links than the 17 | same operations on a singly linked list, the operations are simpler and 18 | potentially more efficient (for nodes other than first nodes) because there 19 | is no need to keep track of the previous node during traversal or no need 20 | to traverse the list to find the previous node, so that its link can be modified. 21 | 22 | ## References 23 | 24 | - [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list) 25 | - [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ&t=7s&index=72&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 26 | -------------------------------------------------------------------------------- /src/algorithms/graph/detect-cycle/__test__/detectDirectedCycle.test.js: -------------------------------------------------------------------------------- 1 | import GraphVertex from '../../../../data-structures/graph/GraphVertex'; 2 | import GraphEdge from '../../../../data-structures/graph/GraphEdge'; 3 | import Graph from '../../../../data-structures/graph/Graph'; 4 | import detectDirectedCycle from '../detectDirectedCycle'; 5 | 6 | describe('detectDirectedCycle', () => { 7 | it('should detect directed cycle', () => { 8 | const vertexA = new GraphVertex('A'); 9 | const vertexB = new GraphVertex('B'); 10 | const vertexC = new GraphVertex('C'); 11 | const vertexD = new GraphVertex('D'); 12 | const vertexE = new GraphVertex('E'); 13 | const vertexF = new GraphVertex('F'); 14 | 15 | const edgeAB = new GraphEdge(vertexA, vertexB); 16 | const edgeBC = new GraphEdge(vertexB, vertexC); 17 | const edgeAC = new GraphEdge(vertexA, vertexC); 18 | const edgeDA = new GraphEdge(vertexD, vertexA); 19 | const edgeDE = new GraphEdge(vertexD, vertexE); 20 | const edgeEF = new GraphEdge(vertexE, vertexF); 21 | const edgeFD = new GraphEdge(vertexF, vertexD); 22 | 23 | const graph = new Graph(true); 24 | graph 25 | .addEdge(edgeAB) 26 | .addEdge(edgeBC) 27 | .addEdge(edgeAC) 28 | .addEdge(edgeDA) 29 | .addEdge(edgeDE) 30 | .addEdge(edgeEF); 31 | 32 | expect(detectDirectedCycle(graph)).toBeNull(); 33 | 34 | graph.addEdge(edgeFD); 35 | 36 | expect(detectDirectedCycle(graph)).toEqual({ 37 | D: vertexF, 38 | F: vertexE, 39 | E: vertexD, 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/data-structures/trie/__test__/TrieNode.test.js: -------------------------------------------------------------------------------- 1 | import TrieNode from '../TrieNode'; 2 | 3 | describe('TrieNode', () => { 4 | it('should create trie node', () => { 5 | const trieNode = new TrieNode('c', true); 6 | 7 | expect(trieNode.character).toBe('c'); 8 | expect(trieNode.isCompleteWord).toBeTruthy(); 9 | expect(trieNode.toString()).toBe('c*'); 10 | }); 11 | 12 | it('should add child nodes', () => { 13 | const trieNode = new TrieNode('c'); 14 | 15 | trieNode.addChild('a', true); 16 | trieNode.addChild('o'); 17 | 18 | expect(trieNode.toString()).toBe('c:a,o'); 19 | }); 20 | 21 | it('should get child nodes', () => { 22 | const trieNode = new TrieNode('c'); 23 | 24 | trieNode.addChild('a'); 25 | trieNode.addChild('o'); 26 | 27 | expect(trieNode.getChild('a').toString()).toBe('a'); 28 | expect(trieNode.getChild('o').toString()).toBe('o'); 29 | expect(trieNode.getChild('b')).toBeUndefined(); 30 | }); 31 | 32 | it('should check if node has specific child', () => { 33 | const trieNode = new TrieNode('c'); 34 | 35 | trieNode.addChild('a'); 36 | trieNode.addChild('o'); 37 | 38 | expect(trieNode.hasChild('a')).toBeTruthy(); 39 | expect(trieNode.hasChild('o')).toBeTruthy(); 40 | expect(trieNode.hasChild('b')).toBeFalsy(); 41 | }); 42 | 43 | it('should suggest next children', () => { 44 | const trieNode = new TrieNode('c'); 45 | 46 | trieNode.addChild('a'); 47 | trieNode.addChild('o'); 48 | 49 | expect(trieNode.suggestChildren()).toEqual(['a', 'o']); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /src/algorithms/string/knuth-morris-pratt/knuthMorrisPratt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://www.youtube.com/watch?v=GTJr8OvyEVQ 3 | * @param {string} word 4 | * @return {number[]} 5 | */ 6 | function buildPatternTable(word) { 7 | const patternTable = [0]; 8 | let prefixIndex = 0; 9 | let suffixIndex = 1; 10 | 11 | while (suffixIndex < word.length) { 12 | if (word[prefixIndex] === word[suffixIndex]) { 13 | patternTable[suffixIndex] = prefixIndex + 1; 14 | suffixIndex += 1; 15 | prefixIndex += 1; 16 | } else if (prefixIndex === 0) { 17 | patternTable[suffixIndex] = 0; 18 | suffixIndex += 1; 19 | } else { 20 | prefixIndex = patternTable[prefixIndex - 1]; 21 | } 22 | } 23 | 24 | return patternTable; 25 | } 26 | 27 | /** 28 | * @param {string} text 29 | * @param {string} word 30 | * @return {number} 31 | */ 32 | export default function knuthMorrisPratt(text, word) { 33 | if (word.length === 0) { 34 | return 0; 35 | } 36 | 37 | let textIndex = 0; 38 | let wordIndex = 0; 39 | 40 | const patternTable = buildPatternTable(word); 41 | 42 | while (textIndex < text.length) { 43 | if (text[textIndex] === word[wordIndex]) { 44 | // We've found a match. 45 | if (wordIndex === word.length - 1) { 46 | return (textIndex - word.length) + 1; 47 | } 48 | wordIndex += 1; 49 | textIndex += 1; 50 | } else if (wordIndex > 0) { 51 | wordIndex = patternTable[wordIndex - 1]; 52 | } else { 53 | wordIndex = 0; 54 | textIndex += 1; 55 | } 56 | } 57 | 58 | return -1; 59 | } 60 | -------------------------------------------------------------------------------- /src/data-structures/graph/__test__/GraphEdge.test.js: -------------------------------------------------------------------------------- 1 | import GraphEdge from '../GraphEdge'; 2 | import GraphVertex from '../GraphVertex'; 3 | 4 | describe('GraphEdge', () => { 5 | it('should create graph edge with default weight', () => { 6 | const startVertex = new GraphVertex('A'); 7 | const endVertex = new GraphVertex('B'); 8 | const edge = new GraphEdge(startVertex, endVertex); 9 | 10 | expect(edge.getKey()).toBe('A_B'); 11 | expect(edge.toString()).toBe('A_B'); 12 | expect(edge.startVertex).toEqual(startVertex); 13 | expect(edge.endVertex).toEqual(endVertex); 14 | expect(edge.weight).toEqual(0); 15 | }); 16 | 17 | it('should create graph edge with predefined weight', () => { 18 | const startVertex = new GraphVertex('A'); 19 | const endVertex = new GraphVertex('B'); 20 | const edge = new GraphEdge(startVertex, endVertex, 10); 21 | 22 | expect(edge.startVertex).toEqual(startVertex); 23 | expect(edge.endVertex).toEqual(endVertex); 24 | expect(edge.weight).toEqual(10); 25 | }); 26 | 27 | it('should be possible to do edge reverse', () => { 28 | const vertexA = new GraphVertex('A'); 29 | const vertexB = new GraphVertex('B'); 30 | const edge = new GraphEdge(vertexA, vertexB, 10); 31 | 32 | expect(edge.startVertex).toEqual(vertexA); 33 | expect(edge.endVertex).toEqual(vertexB); 34 | expect(edge.weight).toEqual(10); 35 | 36 | edge.reverse(); 37 | 38 | expect(edge.startVertex).toEqual(vertexB); 39 | expect(edge.endVertex).toEqual(vertexA); 40 | expect(edge.weight).toEqual(10); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/data-structures/linked-list/__test__/LinkedListNode.test.js: -------------------------------------------------------------------------------- 1 | import LinkedListNode from '../LinkedListNode'; 2 | 3 | describe('LinkedListNode', () => { 4 | it('should create list node with value', () => { 5 | const node = new LinkedListNode(1); 6 | 7 | expect(node.value).toBe(1); 8 | expect(node.next).toBeNull(); 9 | }); 10 | 11 | it('should create list node with object as a value', () => { 12 | const nodeValue = { value: 1, key: 'test' }; 13 | const node = new LinkedListNode(nodeValue); 14 | 15 | expect(node.value.value).toBe(1); 16 | expect(node.value.key).toBe('test'); 17 | expect(node.next).toBeNull(); 18 | }); 19 | 20 | it('should link nodes together', () => { 21 | const node2 = new LinkedListNode(2); 22 | const node1 = new LinkedListNode(1, node2); 23 | 24 | expect(node1.next).toBeDefined(); 25 | expect(node2.next).toBeNull(); 26 | expect(node1.value).toBe(1); 27 | expect(node1.next.value).toBe(2); 28 | }); 29 | 30 | it('should convert node to string', () => { 31 | const node = new LinkedListNode(1); 32 | 33 | expect(node.toString()).toBe('1'); 34 | 35 | node.value = 'string value'; 36 | expect(node.toString()).toBe('string value'); 37 | }); 38 | 39 | it('should convert node to string with custom stringifier', () => { 40 | const nodeValue = { value: 1, key: 'test' }; 41 | const node = new LinkedListNode(nodeValue); 42 | const toStringCallback = value => `value: ${value.value}, key: ${value.key}`; 43 | 44 | expect(node.toString(toStringCallback)).toBe('value: 1, key: test'); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-algorithms-and-data-structures", 3 | "version": "0.0.4", 4 | "description": "Algorithms and data-structures implemented on JavaScript", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "eslint ./src/*", 8 | "test": "jest", 9 | "ci": "npm run lint && npm run test -- --coverage", 10 | "codecov": "codecov" 11 | }, 12 | "pre-commit": [ 13 | "lint", 14 | "test" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/trekhleb/javascript-algorithms.git" 19 | }, 20 | "keywords": [ 21 | "computer-science", 22 | "cs", 23 | "algorithms", 24 | "data-structures", 25 | "javascript", 26 | "algorithm", 27 | "javascript-algorithms", 28 | "sorting-algorithms", 29 | "graph", 30 | "tree" 31 | ], 32 | "author": "Oleksii Trekhleb (https://www.linkedin.com/in/trekhleb/)", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/trekhleb/javascript-algorithms/issues" 36 | }, 37 | "homepage": "https://github.com/trekhleb/javascript-algorithms#readme", 38 | "devDependencies": { 39 | "@types/jest": "^23.1.4", 40 | "babel-cli": "^6.26.0", 41 | "babel-preset-env": "^1.7.0", 42 | "codecov": "^3.0.2", 43 | "eslint": "^4.19.1", 44 | "eslint-config-airbnb": "^17.0.0", 45 | "eslint-plugin-import": "^2.13.0", 46 | "eslint-plugin-jest": "^21.17.0", 47 | "eslint-plugin-jsx-a11y": "^6.1.0", 48 | "eslint-plugin-react": "^7.10.0", 49 | "jest": "^23.3.0", 50 | "pre-commit": "^1.2.2" 51 | }, 52 | "dependencies": {} 53 | } 54 | -------------------------------------------------------------------------------- /src/algorithms/sets/permutations/__test__/permutateWithRepetitions.test.js: -------------------------------------------------------------------------------- 1 | import permutateWithRepetitions from '../permutateWithRepetitions'; 2 | 3 | describe('permutateWithRepetitions', () => { 4 | it('should permutate string with repetition', () => { 5 | const permutations1 = permutateWithRepetitions(['A']); 6 | expect(permutations1).toEqual([ 7 | ['A'], 8 | ]); 9 | 10 | const permutations2 = permutateWithRepetitions(['A', 'B']); 11 | expect(permutations2).toEqual([ 12 | ['A', 'A'], 13 | ['A', 'B'], 14 | ['B', 'A'], 15 | ['B', 'B'], 16 | ]); 17 | 18 | const permutations3 = permutateWithRepetitions(['A', 'B', 'C']); 19 | expect(permutations3).toEqual([ 20 | ['A', 'A', 'A'], 21 | ['A', 'A', 'B'], 22 | ['A', 'A', 'C'], 23 | ['A', 'B', 'A'], 24 | ['A', 'B', 'B'], 25 | ['A', 'B', 'C'], 26 | ['A', 'C', 'A'], 27 | ['A', 'C', 'B'], 28 | ['A', 'C', 'C'], 29 | ['B', 'A', 'A'], 30 | ['B', 'A', 'B'], 31 | ['B', 'A', 'C'], 32 | ['B', 'B', 'A'], 33 | ['B', 'B', 'B'], 34 | ['B', 'B', 'C'], 35 | ['B', 'C', 'A'], 36 | ['B', 'C', 'B'], 37 | ['B', 'C', 'C'], 38 | ['C', 'A', 'A'], 39 | ['C', 'A', 'B'], 40 | ['C', 'A', 'C'], 41 | ['C', 'B', 'A'], 42 | ['C', 'B', 'B'], 43 | ['C', 'B', 'C'], 44 | ['C', 'C', 'A'], 45 | ['C', 'C', 'B'], 46 | ['C', 'C', 'C'], 47 | ]); 48 | 49 | const permutations4 = permutateWithRepetitions(['A', 'B', 'C', 'D']); 50 | expect(permutations4.length).toBe(4 * 4 * 4 * 4); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /src/algorithms/sorting/quick-sort/QuickSort.js: -------------------------------------------------------------------------------- 1 | import Sort from '../Sort'; 2 | 3 | export default class QuickSort extends Sort { 4 | /** 5 | * @param {*[]} originalArray 6 | * @return {*[]} 7 | */ 8 | sort(originalArray) { 9 | // Clone original array to prevent it from modification. 10 | const array = [...originalArray]; 11 | 12 | // If array has less than or equal to one elements then it is already sorted. 13 | if (array.length <= 1) { 14 | return array; 15 | } 16 | 17 | // Init left and right arrays. 18 | const leftArray = []; 19 | const rightArray = []; 20 | 21 | // Take the first element of array as a pivot. 22 | const pivotElement = array.shift(); 23 | const centerArray = [pivotElement]; 24 | 25 | // Split all array elements between left, center and right arrays. 26 | while (array.length) { 27 | const currentElement = array.shift(); 28 | 29 | // Call visiting callback. 30 | this.callbacks.visitingCallback(currentElement); 31 | 32 | if (this.comparator.equal(currentElement, pivotElement)) { 33 | centerArray.push(currentElement); 34 | } else if (this.comparator.lessThan(currentElement, pivotElement)) { 35 | leftArray.push(currentElement); 36 | } else { 37 | rightArray.push(currentElement); 38 | } 39 | } 40 | 41 | // Sort left and right arrays. 42 | const leftArraySorted = this.sort(leftArray); 43 | const rightArraySorted = this.sort(rightArray); 44 | 45 | // Let's now join sorted left array with center array and with sorted right array. 46 | return leftArraySorted.concat(centerArray, rightArraySorted); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/data-structures/trie/__test__/Trie.test.js: -------------------------------------------------------------------------------- 1 | import Trie from '../Trie'; 2 | 3 | describe('Trie', () => { 4 | it('should create trie', () => { 5 | const trie = new Trie(); 6 | 7 | expect(trie).toBeDefined(); 8 | expect(trie.head.toString()).toBe('*'); 9 | }); 10 | 11 | it('should add words to trie', () => { 12 | const trie = new Trie(); 13 | 14 | trie.addWord('cat'); 15 | 16 | expect(trie.head.toString()).toBe('*:c'); 17 | expect(trie.head.getChild('c').toString()).toBe('c:a'); 18 | 19 | trie.addWord('car'); 20 | expect(trie.head.toString()).toBe('*:c'); 21 | expect(trie.head.getChild('c').toString()).toBe('c:a'); 22 | expect(trie.head.getChild('c').getChild('a').toString()).toBe('a:t,r'); 23 | expect(trie.head.getChild('c').getChild('a').getChild('t').toString()).toBe('t*'); 24 | }); 25 | 26 | it('should suggests next characters', () => { 27 | const trie = new Trie(); 28 | 29 | trie.addWord('cat'); 30 | trie.addWord('cats'); 31 | trie.addWord('car'); 32 | trie.addWord('caption'); 33 | 34 | expect(trie.suggestNextCharacters('ca')).toEqual(['t', 'r', 'p']); 35 | expect(trie.suggestNextCharacters('cat')).toEqual(['s']); 36 | expect(trie.suggestNextCharacters('cab')).toBeNull(); 37 | }); 38 | 39 | it('should check if word exists', () => { 40 | const trie = new Trie(); 41 | 42 | trie.addWord('cat'); 43 | trie.addWord('cats'); 44 | trie.addWord('car'); 45 | trie.addWord('caption'); 46 | 47 | expect(trie.doesWordExist('cat')).toBeTruthy(); 48 | expect(trie.doesWordExist('cap')).toBeTruthy(); 49 | expect(trie.doesWordExist('call')).toBeFalsy(); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /src/algorithms/string/regular-expression-matching/README.md: -------------------------------------------------------------------------------- 1 | # Regular Expression Matching 2 | 3 | Given an input string `s` and a pattern `p`, implement regular 4 | expression matching with support for `.` and `*`. 5 | 6 | - `.` Matches any single character. 7 | - `*` Matches zero or more of the preceding element. 8 | 9 | The matching should cover the **entire** input string (not partial). 10 | 11 | **Note** 12 | 13 | - `s` could be empty and contains only lowercase letters `a-z`. 14 | - `p` could be empty and contains only lowercase letters `a-z`, and characters like `.` or `*`. 15 | 16 | ## Examples 17 | 18 | **Example #1** 19 | 20 | Input: 21 | ``` 22 | s = 'aa' 23 | p = 'a' 24 | ``` 25 | 26 | Output: `false` 27 | 28 | Explanation: `a` does not match the entire string `aa`. 29 | 30 | **Example #2** 31 | 32 | Input: 33 | ``` 34 | s = 'aa' 35 | p = 'a*' 36 | ``` 37 | 38 | Output: `true` 39 | 40 | Explanation: `*` means zero or more of the preceding element, `a`. 41 | Therefore, by repeating `a` once, it becomes `aa`. 42 | 43 | **Example #3** 44 | 45 | Input: 46 | 47 | ``` 48 | s = 'ab' 49 | p = '.*' 50 | ``` 51 | 52 | Output: `true` 53 | 54 | Explanation: `.*` means "zero or more (`*`) of any character (`.`)". 55 | 56 | **Example #4** 57 | 58 | Input: 59 | 60 | ``` 61 | s = 'aab' 62 | p = 'c*a*b' 63 | ``` 64 | 65 | Output: `true` 66 | 67 | Explanation: `c` can be repeated 0 times, `a` can be repeated 68 | 1 time. Therefore it matches `aab`. 69 | 70 | ## References 71 | 72 | - [YouTube](https://www.youtube.com/watch?v=l3hda49XcDE&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=71&t=0s) 73 | - [LeetCode](https://leetcode.com/problems/regular-expression-matching/description/) 74 | -------------------------------------------------------------------------------- /src/algorithms/search/interpolation-search/__test__/interpolationSearch.test.js: -------------------------------------------------------------------------------- 1 | import interpolationSearch from '../interpolationSearch'; 2 | 3 | describe('interpolationSearch', () => { 4 | it('should search elements in sorted array of numbers', () => { 5 | expect(interpolationSearch([], 1)).toBe(-1); 6 | expect(interpolationSearch([1], 1)).toBe(0); 7 | expect(interpolationSearch([1], 0)).toBe(-1); 8 | expect(interpolationSearch([1, 1], 1)).toBe(0); 9 | expect(interpolationSearch([1, 2], 1)).toBe(0); 10 | expect(interpolationSearch([1, 2], 2)).toBe(1); 11 | expect(interpolationSearch([10, 20, 30, 40, 50], 40)).toBe(3); 12 | expect(interpolationSearch([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 14)).toBe(13); 13 | expect(interpolationSearch([1, 6, 7, 8, 12, 13, 14, 19, 21, 23, 24, 24, 24, 300], 24)).toBe(10); 14 | expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 600)).toBe(-1); 15 | expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 1)).toBe(0); 16 | expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 2)).toBe(1); 17 | expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 3)).toBe(2); 18 | expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 700)).toBe(3); 19 | expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 800)).toBe(4); 20 | expect(interpolationSearch([0, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 1200)).toBe(5); 21 | expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 19000], 800)).toBe(4); 22 | expect(interpolationSearch([0, 10, 11, 12, 13, 14, 15], 10)).toBe(1); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/algorithms/sorting/merge-sort/README.md: -------------------------------------------------------------------------------- 1 | # Merge Sort 2 | 3 | In computer science, merge sort (also commonly spelled 4 | mergesort) is an efficient, general-purpose, 5 | comparison-based sorting algorithm. Most implementations 6 | produce a stable sort, which means that the implementation 7 | preserves the input order of equal elements in the sorted 8 | output. Mergesort is a divide and conquer algorithm that 9 | was invented by John von Neumann in 1945. 10 | 11 | An example of merge sort. First divide the list into 12 | the smallest unit (1 element), then compare each 13 | element with the adjacent list to sort and merge the 14 | two adjacent lists. Finally all the elements are sorted 15 | and merged. 16 | 17 | ![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif) 18 | 19 | A recursive merge sort algorithm used to sort an array of 7 20 | integer values. These are the steps a human would take to 21 | emulate merge sort (top-down). 22 | 23 | ![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/e/e6/Merge_sort_algorithm_diagram.svg) 24 | 25 | ## Complexity 26 | 27 | | Name | Best | Average | Worst | Memory | Stable | Comments | 28 | | --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- | 29 | | **Merge sort** | n log(n) | n log(n) | n log(n) | n | Yes | | 30 | 31 | ## References 32 | 33 | - [Wikipedia](https://en.wikipedia.org/wiki/Merge_sort) 34 | - [YouTube](https://www.youtube.com/watch?v=KF2j-9iSf4Q&index=27&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 35 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/unique-paths/dpUniquePaths.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DYNAMIC PROGRAMMING approach of solving Unique Paths problem. 3 | * 4 | * @param {number} width - Width of the board. 5 | * @param {number} height - Height of the board. 6 | * @return {number} - Number of unique paths. 7 | */ 8 | export default function dpUniquePaths(width, height) { 9 | // Init board. 10 | const board = Array(height).fill(null).map(() => { 11 | return Array(width).fill(0); 12 | }); 13 | 14 | // Base case. 15 | // There is only one way of getting to board[0][any] and 16 | // there is also only one way of getting to board[any][0]. 17 | // This is because we have a restriction of moving right 18 | // and down only. 19 | for (let rowIndex = 0; rowIndex < height; rowIndex += 1) { 20 | for (let columnIndex = 0; columnIndex < width; columnIndex += 1) { 21 | if (rowIndex === 0 || columnIndex === 0) { 22 | board[rowIndex][columnIndex] = 1; 23 | } 24 | } 25 | } 26 | 27 | // Now, since we have this restriction of moving only to the right 28 | // and down we might say that number of unique paths to the current 29 | // cell is a sum of numbers of unique paths to the cell above the 30 | // current one and to the cell to the left of current one. 31 | for (let rowIndex = 1; rowIndex < height; rowIndex += 1) { 32 | for (let columnIndex = 1; columnIndex < width; columnIndex += 1) { 33 | const uniquesFromTop = board[rowIndex - 1][columnIndex]; 34 | const uniquesFromLeft = board[rowIndex][columnIndex - 1]; 35 | board[rowIndex][columnIndex] = uniquesFromTop + uniquesFromLeft; 36 | } 37 | } 38 | 39 | return board[height - 1][width - 1]; 40 | } 41 | -------------------------------------------------------------------------------- /src/algorithms/graph/topological-sorting/topologicalSort.js: -------------------------------------------------------------------------------- 1 | import Stack from '../../../data-structures/stack/Stack'; 2 | import depthFirstSearch from '../depth-first-search/depthFirstSearch'; 3 | 4 | /** 5 | * @param {Graph} graph 6 | */ 7 | export default function topologicalSort(graph) { 8 | // Create a set of all vertices we want to visit. 9 | const unvisitedSet = {}; 10 | graph.getAllVertices().forEach((vertex) => { 11 | unvisitedSet[vertex.getKey()] = vertex; 12 | }); 13 | 14 | // Create a set for all vertices that we've already visited. 15 | const visitedSet = {}; 16 | 17 | // Create a stack of already ordered vertices. 18 | const sortedStack = new Stack(); 19 | 20 | const dfsCallbacks = { 21 | enterVertex: ({ currentVertex }) => { 22 | // Add vertex to visited set in case if all its children has been explored. 23 | visitedSet[currentVertex.getKey()] = currentVertex; 24 | 25 | // Remove this vertex from unvisited set. 26 | delete unvisitedSet[currentVertex.getKey()]; 27 | }, 28 | leaveVertex: ({ currentVertex }) => { 29 | // If the vertex has been totally explored then we may push it to stack. 30 | sortedStack.push(currentVertex); 31 | }, 32 | allowTraversal: ({ nextVertex }) => { 33 | return !visitedSet[nextVertex.getKey()]; 34 | }, 35 | }; 36 | 37 | // Let's go and do DFS for all unvisited nodes. 38 | while (Object.keys(unvisitedSet).length) { 39 | const currentVertexKey = Object.keys(unvisitedSet)[0]; 40 | const currentVertex = unvisitedSet[currentVertexKey]; 41 | 42 | // Do DFS for current node. 43 | depthFirstSearch(graph, currentVertex, dfsCallbacks); 44 | } 45 | 46 | return sortedStack.toArray(); 47 | } 48 | -------------------------------------------------------------------------------- /src/algorithms/sorting/quick-sort/README.md: -------------------------------------------------------------------------------- 1 | # Quicksort 2 | 3 | Quicksort is a divide and conquer algorithm. 4 | Quicksort first divides a large array into two smaller 5 | sub-arrays: the low elements and the high elements. 6 | Quicksort can then recursively sort the sub-arrays 7 | 8 | The steps are: 9 | 10 | 1. Pick an element, called a pivot, from the array. 11 | 2. Partitioning: reorder the array so that all elements with 12 | values less than the pivot come before the pivot, while all 13 | elements with values greater than the pivot come after it 14 | (equal values can go either way). After this partitioning, 15 | the pivot is in its final position. This is called the 16 | partition operation. 17 | 3. Recursively apply the above steps to the sub-array of 18 | elements with smaller values and separately to the 19 | sub-array of elements with greater values. 20 | 21 | Animated visualization of the quicksort algorithm. 22 | The horizontal lines are pivot values. 23 | 24 | ![Quicksort](https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif) 25 | 26 | ## Complexity 27 | 28 | | Name | Best | Average | Worst | Memory | Stable | Comments | 29 | | --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- | 30 | | **Quick sort** | n log(n) | n log(n) | n2 | log(n) | No | Quicksort is usually done in-place with O(log(n)) stack space | 31 | 32 | ## References 33 | 34 | - [Wikipedia](https://en.wikipedia.org/wiki/Quicksort) 35 | - [YouTube](https://www.youtube.com/watch?v=SLauY6PpjW4&index=28&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 36 | -------------------------------------------------------------------------------- /src/algorithms/graph/eulerian-path/README.md: -------------------------------------------------------------------------------- 1 | # Eulerian Path 2 | 3 | In graph theory, an **Eulerian trail** (or **Eulerian path**) is a 4 | trail in a finite graph which visits every edge exactly once. 5 | Similarly, an **Eulerian circuit** or **Eulerian cycle** is an 6 | Eulerian trail which starts and ends on the same vertex. 7 | 8 | Euler proved that a necessary condition for the existence of Eulerian 9 | circuits is that all vertices in the graph have an even degree, and 10 | stated that connected graphs with all vertices of even degree have 11 | an Eulerian circuit. 12 | 13 | ![Eulerian Circuit](https://upload.wikimedia.org/wikipedia/commons/7/72/Labelled_Eulergraph.svg) 14 | 15 | Every vertex of this graph has an even degree. Therefore, this is 16 | an Eulerian graph. Following the edges in alphabetical order gives 17 | an Eulerian circuit/cycle. 18 | 19 | For the existence of Eulerian trails it is necessary that zero or 20 | two vertices have an odd degree; this means the Königsberg graph 21 | is not Eulerian. If there are no vertices of odd degree, 22 | all Eulerian trails are circuits. If there are exactly two vertices 23 | of odd degree, all Eulerian trails start at one of them and end at 24 | the other. A graph that has an Eulerian trail but not an Eulerian 25 | circuit is called semi-Eulerian. 26 | 27 | ![Königsberg graph](https://upload.wikimedia.org/wikipedia/commons/9/96/K%C3%B6nigsberg_graph.svg) 28 | 29 | The Königsberg Bridges multigraph. This multigraph is not Eulerian, 30 | therefore, a solution does not exist. 31 | 32 | ## References 33 | 34 | - [Wikipedia](https://en.wikipedia.org/wiki/Eulerian_path) 35 | - [YouTube](https://www.youtube.com/watch?v=vvP4Fg4r-Ns&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) 36 | -------------------------------------------------------------------------------- /src/algorithms/graph/bellman-ford/bellmanFord.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {Graph} graph 3 | * @param {GraphVertex} startVertex 4 | * @return {{distances, previousVertices}} 5 | */ 6 | export default function bellmanFord(graph, startVertex) { 7 | const distances = {}; 8 | const previousVertices = {}; 9 | 10 | // Init all distances with infinity assuming that currently we can't reach 11 | // any of the vertices except start one. 12 | distances[startVertex.getKey()] = 0; 13 | graph.getAllVertices().forEach((vertex) => { 14 | previousVertices[vertex.getKey()] = null; 15 | if (vertex.getKey() !== startVertex.getKey()) { 16 | distances[vertex.getKey()] = Infinity; 17 | } 18 | }); 19 | 20 | // We need (|V| - 1) iterations. 21 | for (let iteration = 0; iteration < (graph.getAllVertices().length - 1); iteration += 1) { 22 | // During each iteration go through all vertices. 23 | Object.keys(distances).forEach((vertexKey) => { 24 | const vertex = graph.getVertexByKey(vertexKey); 25 | 26 | // Go through all vertex edges. 27 | graph.getNeighbors(vertex).forEach((neighbor) => { 28 | const edge = graph.findEdge(vertex, neighbor); 29 | // Find out if the distance to the neighbor is shorter in this iteration 30 | // then in previous one. 31 | const distanceToVertex = distances[vertex.getKey()]; 32 | const distanceToNeighbor = distanceToVertex + edge.weight; 33 | if (distanceToNeighbor < distances[neighbor.getKey()]) { 34 | distances[neighbor.getKey()] = distanceToNeighbor; 35 | previousVertices[neighbor.getKey()] = vertex; 36 | } 37 | }); 38 | }); 39 | } 40 | 41 | return { 42 | distances, 43 | previousVertices, 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/algorithms/search/interpolation-search/README.md: -------------------------------------------------------------------------------- 1 | # Interpolation Search 2 | 3 | **Interpolation search** is an algorithm for searching for a key in an array that 4 | has been ordered by numerical values assigned to the keys (key values). 5 | 6 | For example we have a sorted array of `n` uniformly distributed values `arr[]`, 7 | and we need to write a function to search for a particular element `x` in the array. 8 | 9 | **Linear Search** finds the element in `O(n)` time, **Jump Search** takes `O(√ n)` time 10 | and **Binary Search** take `O(Log n)` time. 11 | 12 | The **Interpolation Search** is an improvement over Binary Search for instances, 13 | where the values in a sorted array are _uniformly_ distributed. Binary Search 14 | always goes to the middle element to check. On the other hand, interpolation 15 | search may go to different locations according to the value of the key being 16 | searched. For example, if the value of the key is closer to the last element, 17 | interpolation search is likely to start search toward the end side. 18 | 19 | To find the position to be searched, it uses following formula: 20 | 21 | ``` 22 | // The idea of formula is to return higher value of pos 23 | // when element to be searched is closer to arr[hi]. And 24 | // smaller value when closer to arr[lo] 25 | pos = lo + ((x - arr[lo]) * (hi - lo) / (arr[hi] - arr[Lo])) 26 | 27 | arr[] - Array where elements need to be searched 28 | x - Element to be searched 29 | lo - Starting index in arr[] 30 | hi - Ending index in arr[] 31 | ``` 32 | 33 | ## Complexity 34 | 35 | **Time complexity**: `O(log(log(n))` 36 | 37 | ## References 38 | 39 | - [GeeksForGeeks](https://www.geeksforgeeks.org/interpolation-search/) 40 | - [Wikipedia](https://en.wikipedia.org/wiki/Interpolation_search) 41 | -------------------------------------------------------------------------------- /src/algorithms/search/jump-search/jumpSearch.js: -------------------------------------------------------------------------------- 1 | import Comparator from '../../../utils/comparator/Comparator'; 2 | 3 | /** 4 | * Jump (block) search implementation. 5 | * 6 | * @param {*[]} sortedArray 7 | * @param {*} seekElement 8 | * @param {function(a, b)} [comparatorCallback] 9 | * @return {number} 10 | */ 11 | export default function jumpSearch(sortedArray, seekElement, comparatorCallback) { 12 | const comparator = new Comparator(comparatorCallback); 13 | const arraySize = sortedArray.length; 14 | 15 | if (!arraySize) { 16 | // We can't find anything in empty array. 17 | return -1; 18 | } 19 | 20 | // Calculate optimal jump size. 21 | // Total number of comparisons in the worst case will be ((arraySize/jumpSize) + jumpSize - 1). 22 | // The value of the function ((arraySize/jumpSize) + jumpSize - 1) will be minimum 23 | // when jumpSize = √array.length. 24 | const jumpSize = Math.floor(Math.sqrt(arraySize)); 25 | 26 | // Find the block where the seekElement belong to. 27 | let blockStart = 0; 28 | let blockEnd = jumpSize; 29 | while (comparator.greaterThan(seekElement, sortedArray[Math.min(blockEnd, arraySize) - 1])) { 30 | // Jump to the next block. 31 | blockStart = blockEnd; 32 | blockEnd += jumpSize; 33 | 34 | // If our next block is out of array then we couldn't found the element. 35 | if (blockStart > arraySize) { 36 | return -1; 37 | } 38 | } 39 | 40 | // Do linear search for seekElement in subarray starting from blockStart. 41 | let currentIndex = blockStart; 42 | while (currentIndex < Math.min(blockEnd, arraySize)) { 43 | if (comparator.equal(sortedArray[currentIndex], seekElement)) { 44 | return currentIndex; 45 | } 46 | 47 | currentIndex += 1; 48 | } 49 | 50 | return -1; 51 | } 52 | -------------------------------------------------------------------------------- /src/algorithms/sorting/shell-sort/__test__/ShellSort.test.js: -------------------------------------------------------------------------------- 1 | import ShellSort from '../ShellSort'; 2 | import { 3 | equalArr, 4 | notSortedArr, 5 | reverseArr, 6 | sortedArr, 7 | SortTester, 8 | } from '../../SortTester'; 9 | 10 | // Complexity constants. 11 | const SORTED_ARRAY_VISITING_COUNT = 320; 12 | const NOT_SORTED_ARRAY_VISITING_COUNT = 320; 13 | const REVERSE_SORTED_ARRAY_VISITING_COUNT = 320; 14 | const EQUAL_ARRAY_VISITING_COUNT = 320; 15 | 16 | describe('ShellSort', () => { 17 | it('should sort array', () => { 18 | SortTester.testSort(ShellSort); 19 | }); 20 | 21 | it('should sort array with custom comparator', () => { 22 | SortTester.testSortWithCustomComparator(ShellSort); 23 | }); 24 | 25 | it('should sort negative numbers', () => { 26 | SortTester.testNegativeNumbersSort(ShellSort); 27 | }); 28 | 29 | it('should visit EQUAL array element specified number of times', () => { 30 | SortTester.testAlgorithmTimeComplexity( 31 | ShellSort, 32 | equalArr, 33 | EQUAL_ARRAY_VISITING_COUNT, 34 | ); 35 | }); 36 | 37 | it('should visit SORTED array element specified number of times', () => { 38 | SortTester.testAlgorithmTimeComplexity( 39 | ShellSort, 40 | sortedArr, 41 | SORTED_ARRAY_VISITING_COUNT, 42 | ); 43 | }); 44 | 45 | it('should visit NOT SORTED array element specified number of times', () => { 46 | SortTester.testAlgorithmTimeComplexity( 47 | ShellSort, 48 | notSortedArr, 49 | NOT_SORTED_ARRAY_VISITING_COUNT, 50 | ); 51 | }); 52 | 53 | it('should visit REVERSE SORTED array element specified number of times', () => { 54 | SortTester.testAlgorithmTimeComplexity( 55 | ShellSort, 56 | reverseArr, 57 | REVERSE_SORTED_ARRAY_VISITING_COUNT, 58 | ); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /src/algorithms/tree/depth-first-search/depthFirstSearch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} Callbacks 3 | * @property {function(node: BinaryTreeNode, child: BinaryTreeNode): boolean} allowTraversal - 4 | * Determines whether DFS should traverse from the node to its child. 5 | * @property {function(node: BinaryTreeNode)} enterNode - Called when DFS enters the node. 6 | * @property {function(node: BinaryTreeNode)} leaveNode - Called when DFS leaves the node. 7 | */ 8 | 9 | /** 10 | * @param {Callbacks} [callbacks] 11 | * @returns {Callbacks} 12 | */ 13 | function initCallbacks(callbacks = {}) { 14 | const initiatedCallback = callbacks; 15 | 16 | const stubCallback = () => {}; 17 | const defaultAllowTraversal = () => true; 18 | 19 | initiatedCallback.allowTraversal = callbacks.allowTraversal || defaultAllowTraversal; 20 | initiatedCallback.enterNode = callbacks.enterNode || stubCallback; 21 | initiatedCallback.leaveNode = callbacks.leaveNode || stubCallback; 22 | 23 | return initiatedCallback; 24 | } 25 | 26 | /** 27 | * @param {BinaryTreeNode} node 28 | * @param {Callbacks} callbacks 29 | */ 30 | export function depthFirstSearchRecursive(node, callbacks) { 31 | callbacks.enterNode(node); 32 | 33 | // Traverse left branch. 34 | if (node.left && callbacks.allowTraversal(node, node.left)) { 35 | depthFirstSearchRecursive(node.left, callbacks); 36 | } 37 | 38 | // Traverse right branch. 39 | if (node.right && callbacks.allowTraversal(node, node.right)) { 40 | depthFirstSearchRecursive(node.right, callbacks); 41 | } 42 | 43 | callbacks.leaveNode(node); 44 | } 45 | 46 | /** 47 | * @param {BinaryTreeNode} rootNode 48 | * @param {Callbacks} [callbacks] 49 | */ 50 | export default function depthFirstSearch(rootNode, callbacks) { 51 | depthFirstSearchRecursive(rootNode, initCallbacks(callbacks)); 52 | } 53 | -------------------------------------------------------------------------------- /src/algorithms/search/jump-search/__test__/jumpSearch.test.js: -------------------------------------------------------------------------------- 1 | import jumpSearch from '../jumpSearch'; 2 | 3 | describe('jumpSearch', () => { 4 | it('should search for an element in sorted array', () => { 5 | expect(jumpSearch([], 1)).toBe(-1); 6 | expect(jumpSearch([1], 2)).toBe(-1); 7 | expect(jumpSearch([1], 1)).toBe(0); 8 | expect(jumpSearch([1, 2], 1)).toBe(0); 9 | expect(jumpSearch([1, 2], 1)).toBe(0); 10 | expect(jumpSearch([1, 1, 1], 1)).toBe(0); 11 | expect(jumpSearch([1, 2, 5, 10, 20, 21, 24, 30, 48], 2)).toBe(1); 12 | expect(jumpSearch([1, 2, 5, 10, 20, 21, 24, 30, 48], 0)).toBe(-1); 13 | expect(jumpSearch([1, 2, 5, 10, 20, 21, 24, 30, 48], 0)).toBe(-1); 14 | expect(jumpSearch([1, 2, 5, 10, 20, 21, 24, 30, 48], 7)).toBe(-1); 15 | expect(jumpSearch([1, 2, 5, 10, 20, 21, 24, 30, 48], 5)).toBe(2); 16 | expect(jumpSearch([1, 2, 5, 10, 20, 21, 24, 30, 48], 20)).toBe(4); 17 | expect(jumpSearch([1, 2, 5, 10, 20, 21, 24, 30, 48], 30)).toBe(7); 18 | expect(jumpSearch([1, 2, 5, 10, 20, 21, 24, 30, 48], 48)).toBe(8); 19 | }); 20 | 21 | it('should search object in sorted array', () => { 22 | const sortedArrayOfObjects = [ 23 | { key: 1, value: 'value1' }, 24 | { key: 2, value: 'value2' }, 25 | { key: 3, value: 'value3' }, 26 | ]; 27 | 28 | const comparator = (a, b) => { 29 | if (a.key === b.key) return 0; 30 | return a.key < b.key ? -1 : 1; 31 | }; 32 | 33 | expect(jumpSearch([], { key: 1 }, comparator)).toBe(-1); 34 | expect(jumpSearch(sortedArrayOfObjects, { key: 4 }, comparator)).toBe(-1); 35 | expect(jumpSearch(sortedArrayOfObjects, { key: 1 }, comparator)).toBe(0); 36 | expect(jumpSearch(sortedArrayOfObjects, { key: 2 }, comparator)).toBe(1); 37 | expect(jumpSearch(sortedArrayOfObjects, { key: 3 }, comparator)).toBe(2); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/algorithms/sorting/selection-sort/__test__/SelectionSort.test.js: -------------------------------------------------------------------------------- 1 | import SelectionSort from '../SelectionSort'; 2 | import { 3 | equalArr, 4 | notSortedArr, 5 | reverseArr, 6 | sortedArr, 7 | SortTester, 8 | } from '../../SortTester'; 9 | 10 | // Complexity constants. 11 | const SORTED_ARRAY_VISITING_COUNT = 209; 12 | const NOT_SORTED_ARRAY_VISITING_COUNT = 209; 13 | const REVERSE_SORTED_ARRAY_VISITING_COUNT = 209; 14 | const EQUAL_ARRAY_VISITING_COUNT = 209; 15 | 16 | describe('SelectionSort', () => { 17 | it('should sort array', () => { 18 | SortTester.testSort(SelectionSort); 19 | }); 20 | 21 | it('should sort array with custom comparator', () => { 22 | SortTester.testSortWithCustomComparator(SelectionSort); 23 | }); 24 | 25 | it('should sort negative numbers', () => { 26 | SortTester.testNegativeNumbersSort(SelectionSort); 27 | }); 28 | 29 | it('should visit EQUAL array element specified number of times', () => { 30 | SortTester.testAlgorithmTimeComplexity( 31 | SelectionSort, 32 | equalArr, 33 | EQUAL_ARRAY_VISITING_COUNT, 34 | ); 35 | }); 36 | 37 | it('should visit SORTED array element specified number of times', () => { 38 | SortTester.testAlgorithmTimeComplexity( 39 | SelectionSort, 40 | sortedArr, 41 | SORTED_ARRAY_VISITING_COUNT, 42 | ); 43 | }); 44 | 45 | it('should visit NOT SORTED array element specified number of times', () => { 46 | SortTester.testAlgorithmTimeComplexity( 47 | SelectionSort, 48 | notSortedArr, 49 | NOT_SORTED_ARRAY_VISITING_COUNT, 50 | ); 51 | }); 52 | 53 | it('should visit REVERSE SORTED array element specified number of times', () => { 54 | SortTester.testAlgorithmTimeComplexity( 55 | SelectionSort, 56 | reverseArr, 57 | REVERSE_SORTED_ARRAY_VISITING_COUNT, 58 | ); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /src/data-structures/queue/__test__/Queue.test.js: -------------------------------------------------------------------------------- 1 | import Queue from '../Queue'; 2 | 3 | describe('Queue', () => { 4 | it('should create empty queue', () => { 5 | const queue = new Queue(); 6 | expect(queue).not.toBeNull(); 7 | expect(queue.linkedList).not.toBeNull(); 8 | }); 9 | 10 | it('should enqueue data to queue', () => { 11 | const queue = new Queue(); 12 | 13 | queue.enqueue(1); 14 | queue.enqueue(2); 15 | 16 | expect(queue.toString()).toBe('1,2'); 17 | }); 18 | 19 | it('should be possible to enqueue/dequeue objects', () => { 20 | const queue = new Queue(); 21 | 22 | queue.enqueue({ value: 'test1', key: 'key1' }); 23 | queue.enqueue({ value: 'test2', key: 'key2' }); 24 | 25 | const stringifier = value => `${value.key}:${value.value}`; 26 | 27 | expect(queue.toString(stringifier)).toBe('key1:test1,key2:test2'); 28 | expect(queue.dequeue().value).toBe('test1'); 29 | expect(queue.dequeue().value).toBe('test2'); 30 | }); 31 | 32 | it('should peek data from queue', () => { 33 | const queue = new Queue(); 34 | 35 | expect(queue.peek()).toBeNull(); 36 | 37 | queue.enqueue(1); 38 | queue.enqueue(2); 39 | 40 | expect(queue.peek()).toBe(1); 41 | expect(queue.peek()).toBe(1); 42 | }); 43 | 44 | it('should check if queue is empty', () => { 45 | const queue = new Queue(); 46 | 47 | expect(queue.isEmpty()).toBeTruthy(); 48 | 49 | queue.enqueue(1); 50 | 51 | expect(queue.isEmpty()).toBeFalsy(); 52 | }); 53 | 54 | it('should dequeue from queue in FIFO order', () => { 55 | const queue = new Queue(); 56 | 57 | queue.enqueue(1); 58 | queue.enqueue(2); 59 | 60 | expect(queue.dequeue()).toBe(1); 61 | expect(queue.dequeue()).toBe(2); 62 | expect(queue.dequeue()).toBeNull(); 63 | expect(queue.isEmpty()).toBeTruthy(); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /src/algorithms/sorting/quick-sort/__test__/QuickSortInPlace.test.js: -------------------------------------------------------------------------------- 1 | import QuickSortInPlace from '../QuickSortInPlace'; 2 | import { 3 | equalArr, 4 | notSortedArr, 5 | reverseArr, 6 | sortedArr, 7 | SortTester, 8 | } from '../../SortTester'; 9 | 10 | // Complexity constants. 11 | const SORTED_ARRAY_VISITING_COUNT = 19; 12 | const NOT_SORTED_ARRAY_VISITING_COUNT = 12; 13 | const REVERSE_SORTED_ARRAY_VISITING_COUNT = 19; 14 | const EQUAL_ARRAY_VISITING_COUNT = 19; 15 | 16 | describe('QuickSortInPlace', () => { 17 | it('should sort array', () => { 18 | SortTester.testSort(QuickSortInPlace); 19 | }); 20 | 21 | it('should sort array with custom comparator', () => { 22 | SortTester.testSortWithCustomComparator(QuickSortInPlace); 23 | }); 24 | 25 | it('should sort negative numbers', () => { 26 | SortTester.testNegativeNumbersSort(QuickSortInPlace); 27 | }); 28 | 29 | it('should visit EQUAL array element specified number of times', () => { 30 | SortTester.testAlgorithmTimeComplexity( 31 | QuickSortInPlace, 32 | equalArr, 33 | EQUAL_ARRAY_VISITING_COUNT, 34 | ); 35 | }); 36 | 37 | it('should visit SORTED array element specified number of times', () => { 38 | SortTester.testAlgorithmTimeComplexity( 39 | QuickSortInPlace, 40 | sortedArr, 41 | SORTED_ARRAY_VISITING_COUNT, 42 | ); 43 | }); 44 | 45 | it('should visit NOT SORTED array element specified number of times', () => { 46 | SortTester.testAlgorithmTimeComplexity( 47 | QuickSortInPlace, 48 | notSortedArr, 49 | NOT_SORTED_ARRAY_VISITING_COUNT, 50 | ); 51 | }); 52 | 53 | it('should visit REVERSE SORTED array element specified number of times', () => { 54 | SortTester.testAlgorithmTimeComplexity( 55 | QuickSortInPlace, 56 | reverseArr, 57 | REVERSE_SORTED_ARRAY_VISITING_COUNT, 58 | ); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /src/data-structures/tree/binary-search-tree/README.md: -------------------------------------------------------------------------------- 1 | # Binary Search Tree 2 | 3 | In computer science, binary search trees (BST), sometimes called 4 | ordered or sorted binary trees, are a particular type of container: 5 | data structures that store "items" (such as numbers, names etc.) 6 | in memory. They allow fast lookup, addition and removal of 7 | items, and can be used to implement either dynamic sets of 8 | items, or lookup tables that allow finding an item by its key 9 | (e.g., finding the phone number of a person by name). 10 | 11 | Binary search trees keep their keys in sorted order, so that lookup 12 | and other operations can use the principle of binary search: 13 | when looking for a key in a tree (or a place to insert a new key), 14 | they traverse the tree from root to leaf, making comparisons to 15 | keys stored in the nodes of the tree and deciding, on the basis 16 | of the comparison, to continue searching in the left or right 17 | subtrees. On average, this means that each comparison allows 18 | the operations to skip about half of the tree, so that each 19 | lookup, insertion or deletion takes time proportional to the 20 | logarithm of the number of items stored in the tree. This is 21 | much better than the linear time required to find items by key 22 | in an (unsorted) array, but slower than the corresponding 23 | operations on hash tables. 24 | 25 | A binary search tree of size 9 and depth 3, with 8 at the root. 26 | The leaves are not drawn. 27 | 28 | ![Binary Search Tree](https://upload.wikimedia.org/wikipedia/commons/d/da/Binary_search_tree.svg) 29 | 30 | ## References 31 | 32 | - [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_tree) 33 | - [Inserting to BST on YouTube](https://www.youtube.com/watch?v=wcIRPqTR3Kc&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=9&t=0s) 34 | - [BST Interactive Visualisations](https://www.cs.usfca.edu/~galles/visualization/BST.html) 35 | -------------------------------------------------------------------------------- /src/algorithms/math/liu-hui/liuHui.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Let circleRadius is the radius of circle. 3 | * circleRadius is also the side length of the inscribed hexagon 4 | */ 5 | const circleRadius = 1; 6 | 7 | /** 8 | * @param {number} sideLength 9 | * @param {number} splitCounter 10 | * @return {number} 11 | */ 12 | function getNGonSideLength(sideLength, splitCounter) { 13 | if (splitCounter <= 0) { 14 | return sideLength; 15 | } 16 | 17 | const halfSide = sideLength / 2; 18 | 19 | // Liu Hui used the Gou Gu (Pythagorean theorem) theorem repetitively. 20 | const perpendicular = Math.sqrt((circleRadius ** 2) - (halfSide ** 2)); 21 | const excessRadius = circleRadius - perpendicular; 22 | const splitSideLength = Math.sqrt((excessRadius ** 2) + (halfSide ** 2)); 23 | 24 | return getNGonSideLength(splitSideLength, splitCounter - 1); 25 | } 26 | 27 | /** 28 | * @param {number} splitCount 29 | * @return {number} 30 | */ 31 | function getNGonSideCount(splitCount) { 32 | // Liu Hui began with an inscribed hexagon (6-gon). 33 | const hexagonSidesCount = 6; 34 | 35 | // On every split iteration we make N-gons: 6-gon, 12-gon, 24-gon, 48-gon and so on. 36 | return hexagonSidesCount * (splitCount ? 2 ** splitCount : 1); 37 | } 38 | 39 | /** 40 | * Calculate the π value using Liu Hui's π algorithm 41 | * 42 | * @param {number} splitCount - number of times we're going to split 6-gon. 43 | * On each split we will receive 12-gon, 24-gon and so on. 44 | * @return {number} 45 | */ 46 | export default function liuHui(splitCount = 1) { 47 | const nGonSideLength = getNGonSideLength(circleRadius, splitCount - 1); 48 | const nGonSideCount = getNGonSideCount(splitCount - 1); 49 | const nGonPerimeter = nGonSideLength * nGonSideCount; 50 | const approximateCircleArea = (nGonPerimeter / 2) * circleRadius; 51 | 52 | // Return approximate value of pi. 53 | return approximateCircleArea / (circleRadius ** 2); 54 | } 55 | -------------------------------------------------------------------------------- /src/algorithms/sorting/heap-sort/__test__/HeapSort.test.js: -------------------------------------------------------------------------------- 1 | import HeapSort from '../HeapSort'; 2 | import { 3 | equalArr, 4 | notSortedArr, 5 | reverseArr, 6 | sortedArr, 7 | SortTester, 8 | } from '../../SortTester'; 9 | 10 | // Complexity constants. 11 | // These numbers don't take into account up/dow heapifying of the heap. 12 | // Thus these numbers are higher in reality. 13 | const SORTED_ARRAY_VISITING_COUNT = 40; 14 | const NOT_SORTED_ARRAY_VISITING_COUNT = 40; 15 | const REVERSE_SORTED_ARRAY_VISITING_COUNT = 40; 16 | const EQUAL_ARRAY_VISITING_COUNT = 40; 17 | 18 | describe('HeapSort', () => { 19 | it('should sort array', () => { 20 | SortTester.testSort(HeapSort); 21 | }); 22 | 23 | it('should sort array with custom comparator', () => { 24 | SortTester.testSortWithCustomComparator(HeapSort); 25 | }); 26 | 27 | it('should sort negative numbers', () => { 28 | SortTester.testNegativeNumbersSort(HeapSort); 29 | }); 30 | 31 | it('should visit EQUAL array element specified number of times', () => { 32 | SortTester.testAlgorithmTimeComplexity( 33 | HeapSort, 34 | equalArr, 35 | EQUAL_ARRAY_VISITING_COUNT, 36 | ); 37 | }); 38 | 39 | it('should visit SORTED array element specified number of times', () => { 40 | SortTester.testAlgorithmTimeComplexity( 41 | HeapSort, 42 | sortedArr, 43 | SORTED_ARRAY_VISITING_COUNT, 44 | ); 45 | }); 46 | 47 | it('should visit NOT SORTED array element specified number of times', () => { 48 | SortTester.testAlgorithmTimeComplexity( 49 | HeapSort, 50 | notSortedArr, 51 | NOT_SORTED_ARRAY_VISITING_COUNT, 52 | ); 53 | }); 54 | 55 | it('should visit REVERSE SORTED array element specified number of times', () => { 56 | SortTester.testAlgorithmTimeComplexity( 57 | HeapSort, 58 | reverseArr, 59 | REVERSE_SORTED_ARRAY_VISITING_COUNT, 60 | ); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/algorithms/sorting/merge-sort/__test__/MergeSort.test.js: -------------------------------------------------------------------------------- 1 | import MergeSort from '../MergeSort'; 2 | import { 3 | equalArr, 4 | notSortedArr, 5 | reverseArr, 6 | sortedArr, 7 | SortTester, 8 | } from '../../SortTester'; 9 | 10 | // Complexity constants. 11 | const SORTED_ARRAY_VISITING_COUNT = 79; 12 | const NOT_SORTED_ARRAY_VISITING_COUNT = 102; 13 | const REVERSE_SORTED_ARRAY_VISITING_COUNT = 87; 14 | const EQUAL_ARRAY_VISITING_COUNT = 79; 15 | 16 | describe('MergeSort', () => { 17 | it('should sort array', () => { 18 | SortTester.testSort(MergeSort); 19 | }); 20 | 21 | it('should sort array with custom comparator', () => { 22 | SortTester.testSortWithCustomComparator(MergeSort); 23 | }); 24 | 25 | it('should do stable sorting', () => { 26 | SortTester.testSortStability(MergeSort); 27 | }); 28 | 29 | it('should sort negative numbers', () => { 30 | SortTester.testNegativeNumbersSort(MergeSort); 31 | }); 32 | 33 | it('should visit EQUAL array element specified number of times', () => { 34 | SortTester.testAlgorithmTimeComplexity( 35 | MergeSort, 36 | equalArr, 37 | EQUAL_ARRAY_VISITING_COUNT, 38 | ); 39 | }); 40 | 41 | it('should visit SORTED array element specified number of times', () => { 42 | SortTester.testAlgorithmTimeComplexity( 43 | MergeSort, 44 | sortedArr, 45 | SORTED_ARRAY_VISITING_COUNT, 46 | ); 47 | }); 48 | 49 | it('should visit NOT SORTED array element specified number of times', () => { 50 | SortTester.testAlgorithmTimeComplexity( 51 | MergeSort, 52 | notSortedArr, 53 | NOT_SORTED_ARRAY_VISITING_COUNT, 54 | ); 55 | }); 56 | 57 | it('should visit REVERSE SORTED array element specified number of times', () => { 58 | SortTester.testAlgorithmTimeComplexity( 59 | MergeSort, 60 | reverseArr, 61 | REVERSE_SORTED_ARRAY_VISITING_COUNT, 62 | ); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /src/algorithms/sorting/quick-sort/__test__/QuickSort.test.js: -------------------------------------------------------------------------------- 1 | import QuickSort from '../QuickSort'; 2 | import { 3 | equalArr, 4 | notSortedArr, 5 | reverseArr, 6 | sortedArr, 7 | SortTester, 8 | } from '../../SortTester'; 9 | 10 | // Complexity constants. 11 | const SORTED_ARRAY_VISITING_COUNT = 190; 12 | const NOT_SORTED_ARRAY_VISITING_COUNT = 62; 13 | const REVERSE_SORTED_ARRAY_VISITING_COUNT = 190; 14 | const EQUAL_ARRAY_VISITING_COUNT = 19; 15 | 16 | describe('QuickSort', () => { 17 | it('should sort array', () => { 18 | SortTester.testSort(QuickSort); 19 | }); 20 | 21 | it('should sort array with custom comparator', () => { 22 | SortTester.testSortWithCustomComparator(QuickSort); 23 | }); 24 | 25 | it('should do stable sorting', () => { 26 | SortTester.testSortStability(QuickSort); 27 | }); 28 | 29 | it('should sort negative numbers', () => { 30 | SortTester.testNegativeNumbersSort(QuickSort); 31 | }); 32 | 33 | it('should visit EQUAL array element specified number of times', () => { 34 | SortTester.testAlgorithmTimeComplexity( 35 | QuickSort, 36 | equalArr, 37 | EQUAL_ARRAY_VISITING_COUNT, 38 | ); 39 | }); 40 | 41 | it('should visit SORTED array element specified number of times', () => { 42 | SortTester.testAlgorithmTimeComplexity( 43 | QuickSort, 44 | sortedArr, 45 | SORTED_ARRAY_VISITING_COUNT, 46 | ); 47 | }); 48 | 49 | it('should visit NOT SORTED array element specified number of times', () => { 50 | SortTester.testAlgorithmTimeComplexity( 51 | QuickSort, 52 | notSortedArr, 53 | NOT_SORTED_ARRAY_VISITING_COUNT, 54 | ); 55 | }); 56 | 57 | it('should visit REVERSE SORTED array element specified number of times', () => { 58 | SortTester.testAlgorithmTimeComplexity( 59 | QuickSort, 60 | reverseArr, 61 | REVERSE_SORTED_ARRAY_VISITING_COUNT, 62 | ); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /src/algorithms/uncategorized/jump-game/backtrackingJumpGame.js: -------------------------------------------------------------------------------- 1 | /** 2 | * BACKTRACKING approach of solving Jump Game. 3 | * 4 | * This is the inefficient solution where we try every single jump 5 | * pattern that takes us from the first position to the last. 6 | * We start from the first position and jump to every index that 7 | * is reachable. We repeat the process until last index is reached. 8 | * When stuck, backtrack. 9 | * 10 | * @param {number[]} numbers - array of possible jump length. 11 | * @param {number} startIndex - index from where we start jumping. 12 | * @param {number[]} currentJumps - current jumps path. 13 | * @return {boolean} 14 | */ 15 | export default function backtrackingJumpGame(numbers, startIndex = 0, currentJumps = []) { 16 | if (startIndex === numbers.length - 1) { 17 | // We've jumped directly to last cell. This situation is a solution. 18 | return true; 19 | } 20 | 21 | // Check what the longest jump we could make from current position. 22 | // We don't need to jump beyond the array. 23 | const maxJumpLength = Math.min( 24 | numbers[startIndex], // Jump is within array. 25 | numbers.length - 1 - startIndex, // Jump goes beyond array. 26 | ); 27 | 28 | // Let's start jumping from startIndex and see whether any 29 | // jump is successful and has reached the end of the array. 30 | for (let jumpLength = maxJumpLength; jumpLength > 0; jumpLength -= 1) { 31 | // Try next jump. 32 | const nextIndex = startIndex + jumpLength; 33 | currentJumps.push(nextIndex); 34 | 35 | const isJumpSuccessful = backtrackingJumpGame(numbers, nextIndex, currentJumps); 36 | 37 | // Check if current jump was successful. 38 | if (isJumpSuccessful) { 39 | return true; 40 | } 41 | 42 | // BACKTRACKING. 43 | // If previous jump wasn't successful then retreat and try the next one. 44 | currentJumps.pop(); 45 | } 46 | 47 | return false; 48 | } 49 | -------------------------------------------------------------------------------- /src/algorithms/sets/permutations/README.md: -------------------------------------------------------------------------------- 1 | # Permutations 2 | 3 | When the order doesn't matter, it is a **Combination**. 4 | 5 | When the order **does** matter it is a **Permutation**. 6 | 7 | **"The combination to the safe is 472"**. We do care about the order. `724` won't work, nor will `247`. 8 | It has to be exactly `4-7-2`. 9 | 10 | ## Permutations without repetitions 11 | 12 | A permutation, also called an “arrangement number” or “order”, is a rearrangement of 13 | the elements of an ordered list `S` into a one-to-one correspondence with `S` itself. 14 | 15 | Below are the permutations of string `ABC`. 16 | 17 | `ABC ACB BAC BCA CBA CAB` 18 | 19 | Or for example the first three people in a running race: you can't be first and second. 20 | 21 | **Number of combinations** 22 | 23 | ``` 24 | n * (n-1) * (n -2) * ... * 1 = n! 25 | ``` 26 | 27 | ## Permutations with repetitions 28 | 29 | When repetition is allowed we have permutations with repetitions. 30 | For example the the lock below: it could be `333`. 31 | 32 | ![Permutation Lock](https://www.mathsisfun.com/combinatorics/images/combination-lock.jpg) 33 | 34 | **Number of combinations** 35 | 36 | ``` 37 | n * n * n ... (r times) = n^r 38 | ``` 39 | 40 | ## Cheat Sheets 41 | 42 | Permutations cheat sheet 43 | 44 | ![Permutations Cheat Sheet](https://cdn-images-1.medium.com/max/2000/1*JNK-n0Pt0Vbxk0lxVpgT5A.png) 45 | 46 | Combinations cheat sheet 47 | 48 | ![Combinations Cheat Sheet](https://cdn-images-1.medium.com/max/2000/1*7cFRn8jW4g_91YgDAbmxRQ.png) 49 | 50 | Permutations/combinations algorithm ideas. 51 | 52 | ![Algorithms Idea](https://cdn-images-1.medium.com/max/2000/1*vLsSsZMnesCFPCYTYMbxrQ.png) 53 | 54 | ## References 55 | 56 | - [Math Is Fun](https://www.mathsisfun.com/combinatorics/combinations-permutations.html) 57 | - [Permutations/combinations cheat sheets](https://medium.com/@trekhleb/permutations-combinations-algorithms-cheat-sheet-68c14879aba5) 58 | -------------------------------------------------------------------------------- /src/algorithms/sets/combinations/__test__/combineWithRepetitions.test.js: -------------------------------------------------------------------------------- 1 | import combineWithRepetitions from '../combineWithRepetitions'; 2 | import factorial from '../../../math/factorial/factorial'; 3 | 4 | describe('combineWithRepetitions', () => { 5 | it('should combine string with repetitions', () => { 6 | expect(combineWithRepetitions(['A'], 1)).toEqual([ 7 | ['A'], 8 | ]); 9 | 10 | expect(combineWithRepetitions(['A', 'B'], 1)).toEqual([ 11 | ['A'], 12 | ['B'], 13 | ]); 14 | 15 | expect(combineWithRepetitions(['A', 'B'], 2)).toEqual([ 16 | ['A', 'A'], 17 | ['A', 'B'], 18 | ['B', 'B'], 19 | ]); 20 | 21 | expect(combineWithRepetitions(['A', 'B'], 3)).toEqual([ 22 | ['A', 'A', 'A'], 23 | ['A', 'A', 'B'], 24 | ['A', 'B', 'B'], 25 | ['B', 'B', 'B'], 26 | ]); 27 | 28 | expect(combineWithRepetitions(['A', 'B', 'C'], 2)).toEqual([ 29 | ['A', 'A'], 30 | ['A', 'B'], 31 | ['A', 'C'], 32 | ['B', 'B'], 33 | ['B', 'C'], 34 | ['C', 'C'], 35 | ]); 36 | 37 | expect(combineWithRepetitions(['A', 'B', 'C'], 3)).toEqual([ 38 | ['A', 'A', 'A'], 39 | ['A', 'A', 'B'], 40 | ['A', 'A', 'C'], 41 | ['A', 'B', 'B'], 42 | ['A', 'B', 'C'], 43 | ['A', 'C', 'C'], 44 | ['B', 'B', 'B'], 45 | ['B', 'B', 'C'], 46 | ['B', 'C', 'C'], 47 | ['C', 'C', 'C'], 48 | ]); 49 | 50 | const combinationOptions = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']; 51 | const combinationSlotsNumber = 4; 52 | const combinations = combineWithRepetitions(combinationOptions, combinationSlotsNumber); 53 | const n = combinationOptions.length; 54 | const r = combinationSlotsNumber; 55 | const expectedNumberOfCombinations = factorial((r + n) - 1) / (factorial(r) * factorial(n - 1)); 56 | 57 | expect(combinations.length).toBe(expectedNumberOfCombinations); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /src/algorithms/sorting/bubble-sort/__test__/BubbleSort.test.js: -------------------------------------------------------------------------------- 1 | import BubbleSort from '../BubbleSort'; 2 | import { 3 | equalArr, 4 | notSortedArr, 5 | reverseArr, 6 | sortedArr, 7 | SortTester, 8 | } from '../../SortTester'; 9 | 10 | // Complexity constants. 11 | const SORTED_ARRAY_VISITING_COUNT = 20; 12 | const NOT_SORTED_ARRAY_VISITING_COUNT = 189; 13 | const REVERSE_SORTED_ARRAY_VISITING_COUNT = 209; 14 | const EQUAL_ARRAY_VISITING_COUNT = 20; 15 | 16 | describe('BubbleSort', () => { 17 | it('should sort array', () => { 18 | SortTester.testSort(BubbleSort); 19 | }); 20 | 21 | it('should sort array with custom comparator', () => { 22 | SortTester.testSortWithCustomComparator(BubbleSort); 23 | }); 24 | 25 | it('should do stable sorting', () => { 26 | SortTester.testSortStability(BubbleSort); 27 | }); 28 | 29 | it('should sort negative numbers', () => { 30 | SortTester.testNegativeNumbersSort(BubbleSort); 31 | }); 32 | 33 | it('should visit EQUAL array element specified number of times', () => { 34 | SortTester.testAlgorithmTimeComplexity( 35 | BubbleSort, 36 | equalArr, 37 | EQUAL_ARRAY_VISITING_COUNT, 38 | ); 39 | }); 40 | 41 | it('should visit SORTED array element specified number of times', () => { 42 | SortTester.testAlgorithmTimeComplexity( 43 | BubbleSort, 44 | sortedArr, 45 | SORTED_ARRAY_VISITING_COUNT, 46 | ); 47 | }); 48 | 49 | it('should visit NOT SORTED array element specified number of times', () => { 50 | SortTester.testAlgorithmTimeComplexity( 51 | BubbleSort, 52 | notSortedArr, 53 | NOT_SORTED_ARRAY_VISITING_COUNT, 54 | ); 55 | }); 56 | 57 | it('should visit REVERSE SORTED array element specified number of times', () => { 58 | SortTester.testAlgorithmTimeComplexity( 59 | BubbleSort, 60 | reverseArr, 61 | REVERSE_SORTED_ARRAY_VISITING_COUNT, 62 | ); 63 | }); 64 | }); 65 | --------------------------------------------------------------------------------