├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.ko_KR.md ├── README.md ├── README.zh-CN.md ├── README.zh-TW.md ├── assets └── big-o-graph.png ├── jest.config.js ├── package-lock.json ├── package.json ├── sorting_an_array_of_objects.js └── src ├── algorithms ├── 2darray │ ├── README.md │ └── printspiral.java ├── graph │ ├── articulation-points │ │ ├── README.md │ │ ├── __test__ │ │ │ └── articulationPoints.test.js │ │ └── articulationPoints.js │ ├── bellman-ford │ │ ├── README.md │ │ ├── __test__ │ │ │ └── bellmanFord.test.js │ │ └── bellmanFord.js │ ├── breadth-first-search │ │ ├── README.md │ │ ├── __test__ │ │ │ └── breadthFirstSearch.test.js │ │ └── breadthFirstSearch.js │ ├── bridges │ │ ├── README.md │ │ ├── __test__ │ │ │ └── graphBridges.test.js │ │ └── graphBridges.js │ ├── depth-first-search │ │ ├── README.md │ │ ├── __test__ │ │ │ └── depthFirstSearch.test.js │ │ └── depthFirstSearch.js │ ├── detect-cycle │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── detectDirectedCycle.test.js │ │ │ ├── detectUndirectedCycle.test.js │ │ │ └── detectUndirectedCycleUsingDisjointSet.test.js │ │ ├── detectDirectedCycle.js │ │ ├── detectUndirectedCycle.js │ │ └── detectUndirectedCycleUsingDisjointSet.js │ ├── dijkstra │ │ ├── README.md │ │ ├── __test__ │ │ │ └── dijkstra.test.js │ │ └── dijkstra.js │ ├── eulerian-path │ │ ├── README.md │ │ ├── __test__ │ │ │ └── eulerianPath.test.js │ │ └── eulerianPath.js │ ├── floyd-warshall │ │ ├── README.md │ │ ├── __test__ │ │ │ └── floydWarshall.test.js │ │ └── floydWarshall.js │ ├── hamiltonian-cycle │ │ ├── README.md │ │ ├── __test__ │ │ │ └── hamiltonianCycle.test.js │ │ └── hamiltonianCycle.js │ ├── kruskal │ │ ├── README.md │ │ ├── __test__ │ │ │ └── kruskal.test.js │ │ └── kruskal.js │ ├── prim │ │ ├── README.md │ │ ├── __test__ │ │ │ └── prim.test.js │ │ └── prim.js │ ├── strongly-connected-components │ │ ├── README.md │ │ ├── __test__ │ │ │ └── stronglyConnectedComponents.test.js │ │ └── stronglyConnectedComponents.js │ ├── topological-sorting │ │ ├── README.md │ │ ├── __test__ │ │ │ └── topologicalSort.test.js │ │ └── topologicalSort.js │ └── travelling-salesman │ │ ├── README.md │ │ ├── __test__ │ │ └── bfTravellingSalesman.test.js │ │ └── bfTravellingSalesman.js ├── math │ ├── bits │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── clearBit.test.js │ │ │ ├── divideByTwo.test.js │ │ │ ├── getBit.test.js │ │ │ ├── multiplyByTwo.test.js │ │ │ ├── setBit.test.js │ │ │ ├── switchSign.test.js │ │ │ └── updateBit.test.js │ │ ├── clearBit.js │ │ ├── divideByTwo.js │ │ ├── getBit.js │ │ ├── multiplyByTwo.js │ │ ├── setBit.js │ │ ├── switchSign.js │ │ └── updateBit.js │ ├── euclidean-algorithm │ │ ├── README.md │ │ ├── __test__ │ │ │ └── euclieanAlgorithm.test.js │ │ └── euclideanAlgorithm.js │ ├── factorial │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── factorial.test.js │ │ │ └── factorialRecursive.test.js │ │ ├── factorial.js │ │ └── factorialRecursive.js │ ├── fibonacci │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── fibonacci.test.js │ │ │ └── fibonacciNth.test.js │ │ ├── fibonacci.js │ │ └── fibonacciNth.js │ ├── integer-partition │ │ ├── README.md │ │ ├── __test__ │ │ │ └── integerPartition.test.js │ │ └── integerPartition.js │ ├── is-power-of-two │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── isPowerOfTwo.test.js │ │ │ └── isPowerOfTwoBitwise.test.js │ │ ├── isPowerOfTwo.js │ │ └── isPowerOfTwoBitwise.js │ ├── least-common-multiple │ │ ├── README.md │ │ ├── __test__ │ │ │ └── leastCommonMultiple.test.js │ │ └── leastCommonMultiple.js │ ├── liu-hui │ │ ├── README.md │ │ ├── __test__ │ │ │ └── liuHui.test.js │ │ └── liuHui.js │ ├── pascal-triangle │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── pascalTriangle.test.js │ │ │ └── pascalTriangleRecursive.test.js │ │ ├── pascalTriangle.js │ │ └── pascalTriangleRecursive.js │ ├── primality-test │ │ ├── README.md │ │ ├── __test__ │ │ │ └── trialDivision.test.js │ │ └── trialDivision.js │ └── sieve-of-eratosthenes │ │ ├── README.md │ │ ├── __test__ │ │ └── sieveOfEratosthenes.test.js │ │ └── sieveOfEratosthenes.js ├── search │ ├── binary-search │ │ ├── README.md │ │ ├── __test__ │ │ │ └── binarySearch.test.js │ │ └── binarySearch.js │ ├── interpolation-search │ │ ├── README.md │ │ ├── __test__ │ │ │ └── interpolationSearch.test.js │ │ └── interpolationSearch.js │ ├── jump-search │ │ ├── README.md │ │ ├── __test__ │ │ │ └── jumpSearch.test.js │ │ └── jumpSearch.js │ └── linear-search │ │ ├── README.md │ │ ├── __test__ │ │ └── linearSearch.test.js │ │ └── linearSearch.js ├── sets │ ├── cartesian-product │ │ ├── README.md │ │ ├── __test__ │ │ │ └── cartesianProduct.test.js │ │ └── cartesianProduct.js │ ├── combination-sum │ │ ├── README.md │ │ ├── __test__ │ │ │ └── combinationSum.test.js │ │ └── combinationSum.js │ ├── combinations │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── combineWithRepetitions.test.js │ │ │ └── combineWithoutRepetitions.test.js │ │ ├── combineWithRepetitions.js │ │ └── combineWithoutRepetitions.js │ ├── fisher-yates │ │ ├── README.md │ │ ├── __test__ │ │ │ └── fisherYates.test.js │ │ └── fisherYates.js │ ├── knapsack-problem │ │ ├── Knapsack.js │ │ ├── KnapsackItem.js │ │ ├── README.md │ │ └── __test__ │ │ │ ├── Knapsack.test.js │ │ │ └── KnapsackItem.test.js │ ├── longest-common-subsequence │ │ ├── README.md │ │ ├── __test__ │ │ │ └── longestCommonSubsequence.test.js │ │ └── longestCommonSubsequence.js │ ├── longest-increasing-subsequence │ │ ├── README.md │ │ ├── __test__ │ │ │ └── dpLongestIncreasingSubsequence.test.js │ │ └── dpLongestIncreasingSubsequence.js │ ├── maximum-subarray │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── bfMaximumSubarray.test.js │ │ │ └── dpMaximumSubarray.test.js │ │ ├── bfMaximumSubarray.js │ │ └── dpMaximumSubarray.js │ ├── permutations │ │ ├── README.md │ │ ├── __test__ │ │ │ ├── permutateWithRepetitions.test.js │ │ │ └── permutateWithoutRepetitions.test.js │ │ ├── permutateWithRepetitions.js │ │ └── permutateWithoutRepetitions.js │ ├── power-set │ │ ├── README.md │ │ ├── __test__ │ │ │ └── powerSet.test.js │ │ └── powerSet.js │ └── shortest-common-supersequence │ │ ├── README.md │ │ ├── __test__ │ │ └── shortestCommonSupersequence.test.js │ │ └── shortestCommonSupersequence.js ├── sorting │ ├── Sort.js │ ├── SortTester.js │ ├── __test__ │ │ └── Sort.test.js │ ├── bubble-sort │ │ ├── BubbleSort.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── BubbleSort.test.js │ ├── counting-sort │ │ ├── CountingSort.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── CountingSort.test.js │ ├── heap-sort │ │ ├── HeapSort.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── HeapSort.test.js │ ├── insertion-sort │ │ ├── InsertionSort.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── InsertionSort.test.js │ ├── merge-sort │ │ ├── MergeSort.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── MergeSort.test.js │ ├── quick-sort │ │ ├── QuickSort.js │ │ ├── QuickSortInPlace.js │ │ ├── README.md │ │ └── __test__ │ │ │ ├── QuickSort.test.js │ │ │ └── QuickSortInPlace.test.js │ ├── radix-sort │ │ ├── README.md │ │ ├── RadixSort.js │ │ └── __test__ │ │ │ └── RadixSort.test.js │ ├── selection-sort │ │ ├── README.md │ │ ├── SelectionSort.js │ │ └── __test__ │ │ │ └── SelectionSort.test.js │ └── shell-sort │ │ ├── README.md │ │ ├── ShellSort.js │ │ └── __test__ │ │ └── ShellSort.test.js ├── string │ ├── hamming-distance │ │ ├── README.md │ │ ├── __test__ │ │ │ └── hammingDistance.test.js │ │ └── hammingDistance.js │ ├── knuth-morris-pratt │ │ ├── README.md │ │ ├── __test__ │ │ │ └── knuthMorrisPratt.test.js │ │ └── knuthMorrisPratt.js │ ├── levenshtein-distance │ │ ├── README.md │ │ ├── __test__ │ │ │ └── levenshteinDistance.test.js │ │ └── levenshteinDistance.js │ ├── longest-common-substring │ │ ├── README.md │ │ ├── __test__ │ │ │ └── longestCommonSubstring.test.js │ │ └── longestCommonSubstring.js │ ├── rabin-karp │ │ ├── README.md │ │ ├── __test__ │ │ │ └── rabinKarp.test.js │ │ └── rabinKarp.js │ ├── regular-expression-matching │ │ ├── README.md │ │ ├── __test__ │ │ │ └── regularExpressionMatching.test.js │ │ └── regularExpressionMatching.js │ └── z-algorithm │ │ ├── README.md │ │ ├── __test__ │ │ └── zAlgorithm.test.js │ │ └── zAlgorithm.js ├── tree │ ├── breadth-first-search │ │ ├── README.md │ │ ├── __test__ │ │ │ └── breadthFirstSearch.test.js │ │ └── breadthFirstSearch.js │ └── depth-first-search │ │ ├── README.md │ │ ├── __test__ │ │ └── depthFirstSearch.test.js │ │ └── depthFirstSearch.js └── uncategorized │ ├── hanoi-tower │ ├── README.md │ ├── __test__ │ │ └── hanoiTower.test.js │ └── hanoiTower.js │ ├── jump-game │ ├── README.md │ ├── __test__ │ │ ├── backtrackingJumpGame.test.js │ │ ├── dpBottomUpJumpGame.test.js │ │ ├── dpTopDownJumpGame.test.js │ │ └── greedyJumpGame.test.js │ ├── backtrackingJumpGame.js │ ├── dpBottomUpJumpGame.js │ ├── dpTopDownJumpGame.js │ └── greedyJumpGame.js │ ├── knight-tour │ ├── README.md │ ├── __test__ │ │ └── knightTour.test.js │ └── knightTour.js │ ├── n-queens │ ├── QueenPosition.js │ ├── README.md │ ├── __test__ │ │ ├── QueensPosition.test.js │ │ └── nQueens.test.js │ └── nQueens.js │ ├── square-matrix-rotation │ ├── README.md │ ├── __test__ │ │ └── squareMatrixRotation.test.js │ └── squareMatrixRotation.js │ └── unique-paths │ ├── README.md │ ├── __test__ │ ├── btUniquePaths.test.js │ ├── dpUniquePaths.test.js │ └── uniquePaths.test.js │ ├── btUniquePaths.js │ ├── dpUniquePaths.js │ └── uniquePaths.js ├── data-structures ├── Arrays │ ├── Array.js │ └── README.md ├── bloom-filter │ ├── BloomFilter.js │ ├── README.md │ └── __test__ │ │ └── BloomFilter.test.js ├── disjoint-set │ ├── DisjointSet.js │ ├── DisjointSetItem.js │ ├── README.md │ └── __test__ │ │ ├── DisjointSet.test.js │ │ └── DisjointSetItem.test.js ├── doubly-linked-list │ ├── DoublyLinkedList.js │ ├── DoublyLinkedListNode.js │ ├── README.md │ └── __test__ │ │ ├── DoublyLinkedList.test.js │ │ └── DoublyLinkedListNode.test.js ├── graph │ ├── Graph.js │ ├── GraphEdge.js │ ├── GraphVertex.js │ ├── README.md │ └── __test__ │ │ ├── Graph.test.js │ │ ├── GraphEdge.test.js │ │ └── GraphVertex.test.js ├── hash-table │ ├── HashTable.js │ ├── README.md │ └── __test__ │ │ └── HashTable.test.js ├── heap │ ├── MinHeap.js │ ├── README.md │ └── __test__ │ │ └── MinHeap.test.js ├── linked-list │ ├── LinkedList.js │ ├── LinkedListNode.js │ ├── README.md │ └── __test__ │ │ ├── LinkedList.test.js │ │ └── LinkedListNode.test.js ├── priority-queue │ ├── PriorityQueue.js │ ├── README.md │ └── __test__ │ │ └── PriorityQueue.test.js ├── queue │ ├── Queue.js │ ├── README.md │ └── __test__ │ │ └── Queue.test.js ├── stack │ ├── README.md │ ├── Stack.js │ └── __test__ │ │ └── Stack.test.js ├── tree │ ├── BinaryTreeNode.js │ ├── README.md │ ├── __test__ │ │ └── BinaryTreeNode.test.js │ ├── avl-tree │ │ ├── AvlTree.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── AvlTRee.test.js │ ├── binary-search-tree │ │ ├── BinarySearchTree.js │ │ ├── BinarySearchTreeNode.js │ │ ├── README.md │ │ └── __test__ │ │ │ ├── BinarySearchTree.test.js │ │ │ └── BinarySearchTreeNode.test.js │ ├── fenwick-tree │ │ ├── FenwickTree.js │ │ ├── README.md │ │ └── __test__ │ │ │ └── FenwickTree.test.js │ ├── red-black-tree │ │ ├── README.md │ │ ├── RedBlackTree.js │ │ └── __test__ │ │ │ └── RedBlackTree.test.js │ └── segment-tree │ │ ├── README.md │ │ ├── SegmentTree.js │ │ └── __test__ │ │ └── SegmentTree.test.js └── trie │ ├── README.md │ ├── Trie.js │ ├── TrieNode.js │ └── __test__ │ ├── Trie.test.js │ └── TrieNode.test.js ├── playground ├── README.md ├── __test__ │ └── playground.test.js └── playground.js └── utils └── comparator ├── Comparator.js └── __test__ └── Comparator.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | coverage 4 | .vscode 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /assets/big-o-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zero-to-mastery/javascript-algorithms/9f51350f2239041e364c747e342d6a6cd3ea7a14/assets/big-o-graph.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /sorting_an_array_of_objects.js: -------------------------------------------------------------------------------- 1 | /* Suppose we have an araay of Objects with various properties. 2 | We want to sort the array of those similar objects based on a particular property 3 | or also a combonation of two or more properties. 4 | We will achieve this using the Compare function*/ 5 | 6 | 7 | // An array of a few Soccer Players 8 | 9 | const players = [ 10 | { name: 'Cristiano Ronaldo', team: 'Juventus', age:35 }, 11 | { name: 'Lionel Messi', team: 'Barcelona', age:32 }, 12 | { name: 'Eden Hazard', team: 'Real Madrid', age: 29 }, 13 | { name: 'Neymar Jr', team: 'Paris Saint-German', age:28 }, 14 | ]; 15 | //Note that these statistics can change, these are correct as per 3 May,2020 16 | 17 | /*We can use the following compare function to sort this array of players according to their teams*/ 18 | 19 | function compare(a, b) { 20 | // Use toUpperCase() to ignore character casing 21 | const teamA = a.team.toUpperCase(); 22 | const teamB = b.team.toUpperCase(); 23 | 24 | let comparison = 0; 25 | if (teamA > teamB) { 26 | comparison = 1; 27 | } 28 | else if (teamA < teamB) { 29 | comparison = -1; 30 | } 31 | //If teamA===teamB, no change occurs 32 | 33 | return comparison; 34 | } 35 | //NOTE: A positive value of the return value means teamA comes after teamB and vice-versa 36 | 37 | //Function Call 38 | 39 | players.sort(compare); 40 | 41 | /*returns:- 42 | (4) [{…}, {…}, {…}, {…}] 43 | 0: {name: "Lionel Messi", team: "Barcelona", age: 32} 44 | 1: {name: "Cristiano Ronaldo", team: "Juventus", age: 35} 45 | 2: {name: "Neymar Jr", team: "Paris Saint-German", age: 28} 46 | 3: {name: "Eden Hazard", team: "Real Madrid", age: 29} 47 | length: 4 48 | __proto__: Array(0) 49 | */ 50 | 51 | //Similarly 'compare' function can also be used to sort the players according to their age : Try to figure out yourself :p 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/algorithms/2darray/printspiral.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | public class printspiral{ 3 | public static void spiral(int matrix[][]){ 4 | int startrow=0; 5 | int endrow=matrix.length-1; 6 | int startcol=0; 7 | int endcol=matrix[0].length-1; 8 | while(startrow<=endrow && startcol<=endcol){ 9 | //printing Top boundary 10 | for(int j=startcol;j<=endcol;j++){ 11 | System.out.println(matrix[startrow][j]+" "); 12 | } 13 | //System.out.println(); 14 | //print left extreme 15 | for(int i=startrow+1;i<=endrow;i++){ 16 | System.out.println(matrix[i][endcol]+" "); 17 | 18 | } 19 | //System.out.println(); 20 | //Print bottom boundary 21 | for(int j=endcol-1;j<=startcol;j--){ 22 | if(startrow==endrow){ 23 | break; 24 | } 25 | System.out.println(matrix[endrow][j]+" "); 26 | } 27 | //System.out.println(); 28 | //Print left extreme 29 | for(int i=endrow-1;i<=startrow+1;i--){ 30 | if(startcol==endcol){ 31 | break; 32 | } 33 | System.out.println(matrix[i][startcol]+" "); 34 | } 35 | //System.out.println(); 36 | startrow++; 37 | endrow--; 38 | startcol++; 39 | endcol--; 40 | 41 | 42 | } 43 | //System.out.println(); 44 | 45 | } 46 | public static void main(String args[]){ 47 | int matrix[][]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; 48 | spiral(matrix); 49 | } 50 | } -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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__/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/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/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/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/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/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/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/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/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/multiplyByTwo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} number 3 | * @return {number} 4 | */ 5 | export default function multiplyByTwo(number) { 6 | return number << 1; 7 | } 8 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/__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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/__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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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__/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/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/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/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/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/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/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/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/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/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/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/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/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/data-structures/Arrays/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Arrays 3 | 4 | This data structure organizes data in memory in a sequential manner where data is stored in consecutive memory locations. Because Arrays are mostly used data structures they come inbuilt in many programming languages.In JavaScript, arrays are objects which makes then reference types because they are not primitive types, they are created by the programmer. Arrays are stored in adjucent blocks of memory. 5 | 6 | ## Operations: 7 | 8 | ### Lookup/Access: 9 | 10 | `Iet arr[2]` has time complexity of O(1) because the comp know exactly where the data is (at index 2) 11 | 12 | ### Push(adding elements at the end of the array): 13 | 14 | `Arr.push(“z”)` has time complexity of O(1) because the computer knows the last index. 15 | Can be O(n) for static arrays(has fixed size to add to this array the entire array is copied to a new location with more memory) even with dynamic arrays at rear times. 16 | 17 | ### Pop(removing elements at the end of the array): 18 | 19 | `Arr.pop(“z”)` has time complexity of O(1) because the computer knows the last index 20 | 21 | ### Insert: 22 | 23 | Insertion with arrays has bigO of O(n) because it has to loop through the rest of the array elements to change there indices 24 | 25 | ### Unshift/shift(adding and removing elements at the beginning of the array): 26 | 27 | Adding and removing elements at the beginning of the array has bigO of O(n) because it has to loop through the rest of the array elements to change there indices 28 | 29 | ### Deletion: 30 | 31 | Deleting elements from an array has bigO(n) because it has to loop through the rest of the array elements to change there indices. 32 | So arrays are good for access and adding elements at the end. 33 | 34 | ## References 35 | 36 | - [Medium](https://medium.com/@tonyjemba/the-array-data-structure-in-javascript-6dbbe30c2138) -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/playground/playground.js: -------------------------------------------------------------------------------- 1 | // Place your playground code here. 2 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------