├── .eslintrc ├── .gitignore ├── README.md ├── package-lock.json ├── package.json └── src ├── data-structures ├── array-leetcode │ ├── find-max-consecutive-ones.js │ ├── find-numbers.js │ └── sorted-squares.js ├── binary-heap │ ├── Binary-heap.js │ ├── Max-binary-heap.js │ └── Min-binary-heap.js ├── binary-search-tree │ ├── Binary-search-tree-iterative-version.js │ ├── Binary-search-tree-node.js │ └── Binary-search-tree-recursive-version.js ├── doubly-linked-list │ ├── Doubly-linked-list-node.js │ └── Doubly-linked-list.js ├── graph │ ├── Directed-graph.js │ ├── Graph.js │ └── Undirected-graph.js ├── hashtable │ ├── Hashtable-chaining-arrays.js │ └── Hashtable-chaining-linked-lists.js ├── priority-queue │ ├── Priority-queue-node.js │ └── Priority-queue.js ├── queue │ ├── Queue-from-stack.js │ ├── Queue-node.js │ └── Queue.js ├── singly-linked-list │ ├── Singly-linked-list-node.js │ └── Singly-linked-list.js ├── stack │ ├── Stack-form-queue.js │ ├── Stack-node.js │ └── Stack.js └── trie │ ├── Trie-node.js │ ├── Trie-without-trie-node.js │ └── Trie.js ├── dynamic-programming ├── coin-change.js ├── definition.md ├── fibonacci.js ├── maximum-subarray.js ├── min-coin-change.js └── stairs.js ├── other-algorithms └── factorial-of-large-number.js ├── problem-solving-patterns ├── char_counter.js ├── definition.md ├── divide-and-conquer │ ├── count-zeroes.js │ ├── definition.md │ ├── find-rotated-index.js │ ├── maximum-subarray.js │ └── sorted-frequency.js ├── frequency-counter │ ├── 0-pattern.js │ ├── are-there-duplicates.js │ ├── construct-note.js │ ├── definition.md │ ├── find-all-duplicates.js │ ├── find-pair.js │ ├── same-array.js │ ├── same-frequency.js │ └── valid_anagram.js ├── is_alpha_numeric.js ├── multiple-pointers │ ├── are-there-duplicates.js │ ├── average-pair.js │ ├── count-unique-values.js │ ├── definition.md │ ├── find-pair.js │ ├── is-subsequence.js │ └── sum-zero.js └── sliding-window │ ├── definition.md │ ├── find-longest-substring.js │ ├── max-subarray-sum.js │ └── min-suarray-length.js ├── recursion ├── capitalize-first.js ├── capitalize-words.js ├── collect-strings.js ├── definition.md ├── factorial.js ├── fibonacci.js ├── flatten.js ├── helper_method_recursion │ ├── collect_odd_values.js │ └── pattern.js ├── is-palindrome.js ├── nested-even-sum.js ├── power.js ├── product-of-array.js ├── recursive-range.js ├── reverse.js ├── some-recursive.js └── stringify-numbers.js ├── rosetta-code ├── 100-doors.js ├── 9-billion-names-of-god.js ├── abc-problem.js ├── abundant-deficient-and-perfect-number-classifications.js ├── accumulator-factory.js ├── ackermann-function.js ├── align-columns.js ├── amicable-pairs.js ├── averages-mode.js ├── averages-pythagorean-means.js ├── averages-root-mean-square.js ├── babbage-problem.js ├── balanced-brackets.js ├── combinations.js ├── comma-quibbling.js ├── compare-a-list-of-strings.js ├── convert-seconds-to-compound-duration.js ├── count-occurrences-of-a-substring.js ├── count-the-coins.js ├── date-format.js ├── day-of-the-week.js ├── deepcopy.js ├── define-a-primitive-data-type.js ├── emirp-primes.js ├── entropy.js ├── equilibrium-index.js ├── ethiopian-multiplication.js ├── evaluate-binomial-coefficients.js ├── factorial.js ├── factors-of-an-integer.js ├── farey-sequence.js ├── fibonacci-n-step-number-sequences.js ├── fibonacci-sequence.js ├── fibonacci-word.js ├── general-fizzbuzz.js ├── generate-lower-case-ASCII-alphabet.js ├── generator-exponential.js ├── gray-code.js ├── greatest-common-divisor.js ├── greatest-subsequential-sum.js ├── hailstone-sequence.js ├── happy-numbers.js ├── harshad-or-niven-series.js ├── hash-from-two-arrays.js ├── heronian-triangles.js ├── hofstadter-Q-sequence.js ├── hofstadter-figure-figure-sequences.js ├── i-before-e-except-after-c.js ├── iterated-digits-squaring.js ├── josephus-problem.js ├── tokenize-a-string-with-escaping.js ├── top-rank-per-group.js ├── topological-sort.js ├── towers-of-hanoi.js ├── vector-cross-product.js └── vector-dot-product.js ├── searching-algorithms ├── binary-search.js ├── knuth-morris-pratt-search.js ├── linear-search.js └── naive-string-search.js └── sorting-algorithms ├── bubble-sort ├── bubble-sort-comparator.js └── bubble-sort.js ├── insertion-sort ├── insertion-sort-comparator.js └── insertion-sort.js ├── merge-sort ├── merge-sort-comparator.js ├── merge-sort-modify-original-array.js ├── merge-sort-modify-parameters.js └── merge-sort-not-modify-paramentrs.js ├── quick-sort ├── quick-sort-comparator.js └── quick-sort.js ├── radix-sort └── radix-sort.js └── selection-sort ├── selection-sort-comparator.js └── selection-sort.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "standard" 5 | ], 6 | "rules": { 7 | "space-before-function-paren": 0, 8 | "semi": [ 9 | 2, 10 | "always" 11 | ] 12 | }, 13 | "env": { 14 | "node": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.idea 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## JavaScript Algorithms and Data Structures Masterclass on Udemy 2 | ##### Instructor: Colt Steele 3 | Link to course: https://www.udemy.com/js-algorithms-and-data-structures-masterclass/?persist_locale&locale=en_US 4 | 5 | This repo contains solutions to Colt Steele's JavaScript Algorithms and Data Structures Masterclass course on Udemy. 6 | 7 | The [original repo](https://github.com/NKaty/Algorithms-and-Data-Structures) is made by [NKaty](https://github.com/NKaty). This [revised repo](https://github.com/Violet-Bora-Lee/javascript-algorithms-and-data-structure) is made by [Violet-Bora-Lee](https://github.com/Violet-Bora-Lee). 8 | 9 | #### Coding Exercises of the course: 10 | - [Problem solving patterns](src/problem-solving-patterns) 11 | - [Frequency Counter Pattern](src/problem-solving-patterns/frequency-counter) 12 | - [Multiple Pointers Pattern](src/problem-solving-patterns/multiple-pointers) 13 | - [Sliding Window Pattern](src/problem-solving-patterns/sliding-window) 14 | - [Divide And Conquer Pattern](src/problem-solving-patterns/divide-and-conquer) 15 | - [Recursion](src/recursion) 16 | - [Searching Algorithms](src/searching-algorithms) 17 | - [Binary Search](src/searching-algorithms/binary-search.js) 18 | - [Linear Search](src/searching-algorithms/linear-search.js) 19 | - [Naive String Search](src/searching-algorithms/naive-string-search.js) 20 | - [Knuth–Morris–Pratt Algorithm](src/searching-algorithms/knuth-morris-pratt-search.js) 21 | - [Sorting Algorithms](src/sorting-algorithms) 22 | - [Bubble Sort](src/sorting-algorithms/bubble-sort) 23 | - [Selection Sort](src/sorting-algorithms/selection-sort) 24 | - [Insertion Sort](src/sorting-algorithms/insertion-sort) 25 | - [Merge Sort](src/sorting-algorithms/merge-sort) 26 | - [Quick Sort](src/sorting-algorithms/quick-sort) 27 | - [Radix Sort](src/sorting-algorithms/radix-sort) 28 | - [Data Structures](src/data-structures) 29 | - [Singly Linked List](src/data-structures/singly-linked-list) 30 | - [Doubly Linked List](src/data-structures/doubly-linked-list) 31 | - [Stack](src/data-structures/stack) 32 | - [Queue](src/data-structures/queue) 33 | - [Binary Search Tree](src/data-structures/binary-search-tree) 34 | - [Binary Heap](src/data-structures/binary-heap) 35 | - [Priority Queue](src/data-structures/priority-queue) 36 | - [Hash Table](src/data-structures/hashtable) 37 | - [Graph](src/data-structures/graph) 38 | - [Trie](src/data-structures/trie) 39 | - [Dynamic Programming](src/dynamic-programming) 40 | - [Other algorithms](src/other-algorithms) 41 | - [Factorial of large number](src/other-algorithms/factorial-of-large-number.js) 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "algorithms-and-data-structures", 3 | "version": "1.0.0", 4 | "description": "Implementation of Algorithms and Data Structures on JavaScript", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/NKaty/Algorithms-and-Data-Structures.git" 9 | }, 10 | "scripts": { 11 | "lint": "eslint ./src/*" 12 | }, 13 | "pre-commit": [ 14 | "lint" 15 | ], 16 | "author": "NKaty", 17 | "license": "ISC", 18 | "devDependencies": { 19 | "eslint": "^6.6.0", 20 | "eslint-config-standard": "^14.1.0", 21 | "eslint-plugin-import": "^2.18.2", 22 | "eslint-plugin-node": "^10.0.0", 23 | "eslint-plugin-promise": "^4.2.1", 24 | "eslint-plugin-standard": "^4.0.1", 25 | "pre-commit": "^1.2.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/data-structures/array-leetcode/find-max-consecutive-ones.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/explore/learn/card/fun-with-arrays/521/introduction/3238/ 2 | 3 | // Max Consecutive Ones 4 | 5 | // 6 | // Given a binary array, find the maximum number of consecutive 1s in this array. 7 | // 8 | // Example 1: 9 | // Input: [1,1,0,1,1,1] 10 | // Output: 3 11 | // Explanation: The first two digits or the last three digits are consecutive 1s. 12 | // The maximum number of consecutive 1s is 3. 13 | // Note: 14 | // 15 | // The input array will only contain 0 and 1. 16 | // The length of input array is a positive integer and will not exceed 10,000 17 | 18 | /** 19 | * @param {number[]} nums 20 | * @return {number} 21 | */ 22 | const findMaxConsecutiveOnes = function(nums) { 23 | let count = 0; 24 | let maxCount = 0; 25 | 26 | for (let i = 0; i < nums.length; i++) { 27 | if(nums[i] === 1){ 28 | count++; 29 | } else { 30 | maxCount = Math.max(count, maxCount); 31 | count = 0; 32 | } 33 | } 34 | return Math.max(count, maxCount); 35 | }; 36 | -------------------------------------------------------------------------------- /src/data-structures/array-leetcode/find-numbers.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/explore/learn/card/fun-with-arrays/521/introduction/3237/ 2 | // 3 | // Find Numbers with Even Number of Digits 4 | // 5 | // Given an array nums of integers, return how many of them contain an even number of digits. 6 | // 7 | // 8 | // Example 1: 9 | // 10 | // Input: nums = [12,345,2,6,7896] 11 | // Output: 2 12 | // Explanation: 13 | // 12 contains 2 digits (even number of digits). 14 | // 345 contains 3 digits (odd number of digits). 15 | // 2 contains 1 digit (odd number of digits). 16 | // 6 contains 1 digit (odd number of digits). 17 | // 7896 contains 4 digits (even number of digits). 18 | // Therefore only 12 and 7896 contain an even number of digits. 19 | // Example 2: 20 | // 21 | // Input: nums = [555,901,482,1771] 22 | // Output: 1 23 | // Explanation: 24 | // Only 1771 contains an even number of digits. 25 | // 26 | // 27 | // Constraints: 28 | // 29 | // 1 <= nums.length <= 500 30 | // 1 <= nums[i] <= 10^5 31 | 32 | /** 33 | * @param {number[]} nums 34 | * @return {number} 35 | */ 36 | 37 | // my first try 38 | // const findNumbers = function(nums) { 39 | // let count = 0; 40 | // 41 | // for (let i = 0; i < nums.length; i++) { 42 | // const length = nums[i].toString().length; 43 | // if(length%2 === 0) count++; 44 | // } 45 | // 46 | // return count; 47 | // 48 | // }; 49 | 50 | // most voted solution 51 | // It would be better to pay attention on constraints! 52 | const findNumbers = function(nums) { 53 | let count = 0; 54 | 55 | for (let i = 0; i < nums.length; i++) { 56 | // 10~99, 1000~9999, 100000 57 | if((10 <= nums[i] && nums[i] < 100) || (1000 <= nums[i] && nums[i] < 10000) || nums[i] === 100000) 58 | count++; 59 | } 60 | 61 | return count; 62 | 63 | }; 64 | -------------------------------------------------------------------------------- /src/data-structures/array-leetcode/sorted-squares.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/explore/learn/card/fun-with-arrays/521/introduction/3240/ 2 | // 3 | // Squares of a Sorted Array 4 | // 5 | // Given an array of integers A sorted in non-decreasing order, return an array of the squares of each number, also in sorted non-decreasing order. 6 | // 7 | // 8 | // 9 | // Example 1: 10 | // 11 | // Input: [-4,-1,0,3,10] 12 | // Output: [0,1,9,16,100] 13 | // Example 2: 14 | // 15 | // Input: [-7,-3,2,3,11] 16 | // Output: [4,9,9,49,121] 17 | // 18 | // 19 | // Note: 20 | // 21 | // 1 <= A.length <= 10000 22 | // -10000 <= A[i] <= 10000 23 | // A is sorted in non-decreasing order. 24 | 25 | /** 26 | * @param {number[]} A 27 | * @return {number[]} 28 | */ 29 | const sortedSquares = function(A) { 30 | return A.map(num => Math.pow(Math.abs(num), 2)).sort(compareNumeric); 31 | }; 32 | 33 | const compareNumeric = (a, b) => { 34 | if (a > b) return 1; 35 | if (a === b) return 0; 36 | if (a < b) return -1; 37 | } 38 | -------------------------------------------------------------------------------- /src/data-structures/binary-heap/Binary-heap.js: -------------------------------------------------------------------------------- 1 | // BinaryHeap 2 | 3 | // Implement the following functions on the MaxBinaryHeap/MinBinaryHeap class 4 | 5 | // insert 6 | // This function should insert a node in a binary heap. 7 | // Make sure to re-order the heap after insertion if necessary. 8 | 9 | // extractMax/Min 10 | // This function should remove the root node in a binary heap. 11 | // Make sure to re-order the heap after removal if necessary. 12 | 13 | // heapify 14 | // This function should convert array into a max/min heap. 15 | // Should return the values property on the heap. 16 | 17 | // heapSort 18 | // This function should sort array with Time complexity O(n * log n) and 19 | // Space complexity O(1). Should return the values property on the heap. 20 | 21 | // Additionally, the following methods are implemented on the class: 22 | // find - returns an array of indexes of items with the given value 23 | // remove - removes all items with the given value 24 | 25 | class BinaryHeap { 26 | constructor() { 27 | if (new.target === BinaryHeap) { 28 | throw new TypeError('You cannot instantiate BinaryHeap class directly'); 29 | } 30 | 31 | this.values = []; 32 | this.getItem = this.getItem.bind(this); 33 | } 34 | 35 | getLeftChildIndex(parentIndex) { 36 | return (2 * parentIndex) + 1; 37 | } 38 | 39 | getRightChildIndex(parentIndex) { 40 | return (2 * parentIndex) + 2; 41 | } 42 | 43 | getParentIndex(childIndex) { 44 | return Math.floor((childIndex - 1) / 2); 45 | } 46 | 47 | getItem(index) { 48 | return this.values[index]; 49 | } 50 | 51 | insert(value) { 52 | this.values.push(value); 53 | this.bubbleUp(); 54 | 55 | return this; 56 | } 57 | 58 | extract() { 59 | if (this.isEmpty()) return null; 60 | if (this.values.length === 1) return this.values.pop(); 61 | 62 | this.swap(0, this.values.length - 1); 63 | const extractedItem = this.values.pop(); 64 | this.sinkDown(); 65 | 66 | return extractedItem; 67 | } 68 | 69 | peek() { 70 | if (this.isEmpty()) return null; 71 | 72 | return this.values[0]; 73 | } 74 | 75 | find(value, getValueFn = this.getItem) { 76 | const indexes = []; 77 | 78 | for (let i = 0; i < this.values.length; i++) { 79 | if (getValueFn(i) === value) indexes.push(i); 80 | } 81 | 82 | return indexes; 83 | } 84 | 85 | remove(value, getValueFn = this.getItem) { 86 | const numberItems = this.find(value, getValueFn).length; 87 | 88 | for (let i = 0; i < numberItems; i++) { 89 | const itemIndex = this.find(value, getValueFn)[0]; 90 | 91 | if (this.values.length === 1 || itemIndex === this.values.length - 1) { 92 | this.values.pop(); 93 | return this; 94 | } 95 | 96 | this.swap(itemIndex, this.values.length - 1); 97 | this.values.pop(); 98 | const swapItemValue = this.getItem(itemIndex); 99 | const parentIndex = this.getParentIndex(itemIndex); 100 | 101 | if (parentIndex >= 0 && this.compare(swapItemValue, this.getItem(parentIndex))) { 102 | this.bubbleUp(itemIndex); 103 | } else { 104 | this.sinkDown(itemIndex); 105 | } 106 | } 107 | 108 | return this; 109 | } 110 | 111 | heapify(array = this.values, index = array.length) { 112 | if (array === this.values) { 113 | for (let i = 0; i < index; i++) { 114 | this.bubbleUp(i); 115 | } 116 | } else { 117 | for (let i = 0; i < index; i++) { 118 | this.insert(array[i]); 119 | } 120 | } 121 | 122 | return this.values; 123 | } 124 | 125 | heapSort(array = this.values) { 126 | const length = array.length; 127 | 128 | if (array !== this.values) this.heapify(array); 129 | 130 | for (let i = 1; i < length; i++) { 131 | this.swap(0, length - i); 132 | this.heapify(this.values, length - i); 133 | } 134 | 135 | return this.values.reverse(); 136 | } 137 | 138 | heapSortViaExtract() { 139 | const sortedArray = []; 140 | const valuesCopy = [...this.values]; 141 | 142 | while (this.values.length) { 143 | sortedArray.push(this.extract()); 144 | }; 145 | 146 | this.values = [...valuesCopy]; 147 | 148 | return sortedArray; 149 | } 150 | 151 | bubbleUp(idx = this.values.length - 1) { 152 | let index = idx; 153 | const value = this.getItem(index); 154 | 155 | while (index > 0) { 156 | const parentIndex = this.getParentIndex(index); 157 | 158 | if (this.compare(this.getItem(parentIndex), value)) break; 159 | 160 | this.swap(index, parentIndex); 161 | index = parentIndex; 162 | } 163 | } 164 | 165 | sinkDown(idx = 0) { 166 | const length = this.values.length; 167 | let index = idx; 168 | 169 | while (true) { 170 | const leftChildIndex = this.getLeftChildIndex(index); 171 | const rightChildIndex = this.getRightChildIndex(index); 172 | 173 | if (leftChildIndex >= length && leftChildIndex >= length) break; 174 | 175 | if (rightChildIndex >= length && 176 | this.compare(this.getItem(index), this.getItem(leftChildIndex))) break; 177 | 178 | if (this.compare(this.getItem(index), this.getItem(leftChildIndex)) && 179 | this.compare(this.getItem(index), this.getItem(rightChildIndex))) break; 180 | 181 | const swapIndex = rightChildIndex >= length || 182 | this.compare(this.getItem(leftChildIndex), this.getItem(rightChildIndex)) 183 | ? leftChildIndex : rightChildIndex; 184 | 185 | this.swap(index, swapIndex); 186 | index = swapIndex; 187 | } 188 | } 189 | 190 | isEmpty() { 191 | return !this.values.length; 192 | } 193 | 194 | swap(indexOne, indexTwo) { 195 | [this.values[indexOne], this.values[indexTwo]] = 196 | [this.values[indexTwo], this.values[indexOne]]; 197 | } 198 | 199 | compare(firstItem, secondItem) { 200 | throw new Error('You must implement your own compare method for min or max heap.'); 201 | } 202 | } 203 | 204 | module.exports = BinaryHeap; 205 | -------------------------------------------------------------------------------- /src/data-structures/binary-heap/Max-binary-heap.js: -------------------------------------------------------------------------------- 1 | const BinaryHeap = require('./Binary-heap'); 2 | 3 | class MaxBinaryHeap extends BinaryHeap { 4 | extractMax() { 5 | return super.extract(); 6 | } 7 | 8 | compare(firstItem, secondItem) { 9 | return firstItem >= secondItem; 10 | } 11 | } 12 | 13 | const binaryHeap1 = new MaxBinaryHeap().insert(1).insert(2).insert(3).insert(4).insert(5).insert(6).insert(4); 14 | 15 | console.log(binaryHeap1.values); // [ 6, 4, 5, 1, 3, 2, 4 ] 16 | console.log(binaryHeap1.find(4)); // [ 1, 6 ] 17 | binaryHeap1.remove(4); 18 | console.log(binaryHeap1.values); // [ 6, 3, 5, 1, 2 ] 19 | binaryHeap1.insert(4); 20 | console.log(binaryHeap1.values); // [ 6, 3, 5, 1, 2, 4 ] 21 | console.log(binaryHeap1.extractMax()); // 6 22 | console.log(binaryHeap1.values); // [ 5, 3, 4, 1, 2 ] 23 | console.log(binaryHeap1.heapSortViaExtract()); 24 | console.log(binaryHeap1.values); 25 | 26 | const binaryHeap2 = new MaxBinaryHeap(); 27 | 28 | binaryHeap2.heapify([7, 4, 9, 6, 1, 8, 4]); 29 | console.log(binaryHeap2.values); // [ 9, 6, 8, 4, 1, 7, 4 ] 30 | binaryHeap2.heapSort(); 31 | console.log(binaryHeap2.values); // [ 9, 8, 7, 6, 4, 4, 1 ] 32 | -------------------------------------------------------------------------------- /src/data-structures/binary-heap/Min-binary-heap.js: -------------------------------------------------------------------------------- 1 | const BinaryHeap = require('./Binary-heap'); 2 | 3 | class MinBinaryHeap extends BinaryHeap { 4 | extractMin() { 5 | return super.extract(); 6 | } 7 | 8 | compare(firstItem, secondItem) { 9 | return firstItem <= secondItem; 10 | } 11 | } 12 | 13 | if (module.parent) { 14 | module.exports = MinBinaryHeap; 15 | } else { 16 | const binaryHeap1 = new MinBinaryHeap().insert(1).insert(2).insert(3).insert(4).insert(5).insert(6).insert(4); 17 | 18 | console.log(binaryHeap1.values); // [ 1, 2, 3, 4, 5, 6, 4 ] 19 | console.log(binaryHeap1.find(4)); // [ 3, 6 ] 20 | binaryHeap1.remove(4); 21 | console.log(binaryHeap1.values); // [[ 1, 2, 3, 6, 5 ] 22 | binaryHeap1.insert(4); 23 | console.log(binaryHeap1.values); // [ 1, 2, 3, 6, 5, 4 ] 24 | console.log(binaryHeap1.extractMin()); // 1 25 | console.log(binaryHeap1.values); // [ 2, 4, 3, 6, 5 ] 26 | console.log(binaryHeap1.heapSortViaExtract()); 27 | console.log(binaryHeap1.values); 28 | 29 | const binaryHeap2 = new MinBinaryHeap(); 30 | 31 | binaryHeap2.heapify([7, 4, 9, 6, 1, 8, 4]); 32 | console.log(binaryHeap2.values); // [ 1, 4, 4, 7, 6, 9, 8 ] 33 | binaryHeap2.heapSort(); 34 | console.log(binaryHeap2.values); // [ 1, 4, 4, 6, 7, 8, 9 ] 35 | } 36 | -------------------------------------------------------------------------------- /src/data-structures/binary-search-tree/Binary-search-tree-node.js: -------------------------------------------------------------------------------- 1 | module.exports = class BinarySearchTreeNode { 2 | constructor(data) { 3 | this.data = data; 4 | this.left = null; 5 | this.right = null; 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /src/data-structures/doubly-linked-list/Doubly-linked-list-node.js: -------------------------------------------------------------------------------- 1 | module.exports = class DoublyLinkedListNode { 2 | constructor(data, next = null, prev = null) { 3 | this.data = data; 4 | this.next = next; 5 | this.prev = prev; 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /src/data-structures/graph/Undirected-graph.js: -------------------------------------------------------------------------------- 1 | // UndirectedGraph 2 | 3 | // Implement the following methods on the UndirectedGraph class 4 | 5 | // addEdge 6 | // This function should add an edge between two nodes in the graph and place 7 | // each value of the nodes in each array for the value of the node in the adjacency list. 8 | 9 | // removeEdge 10 | // This function should accept two nodes and remove the edge between them. 11 | // It should modify the adjacency list to ensure that both values are not 12 | // in each array for the two nodes which no longer contain the edge. 13 | 14 | // hasCycle 15 | // This function should return true if the graph contains a cycle or false if not. 16 | 17 | const Graph = require('./Graph'); 18 | 19 | class UndirectedGraph extends Graph { 20 | addEdge(vertexOne, vertexTwo, weight = 0) { 21 | if (!this.adjacencyList[vertexOne]) this.adjacencyList[vertexOne] = []; 22 | if (!this.adjacencyList[vertexTwo]) this.adjacencyList[vertexTwo] = []; 23 | 24 | if (!this.adjacencyList[vertexOne].includes(vertexTwo)) { 25 | this.adjacencyList[vertexOne].push({ value: vertexTwo, weight }); 26 | } 27 | if (!this.adjacencyList[vertexTwo].includes(vertexOne)) { 28 | this.adjacencyList[vertexTwo].push({ value: vertexOne, weight }); 29 | } 30 | } 31 | 32 | removeEdge(vertexOne, vertexTwo) { 33 | if (this.adjacencyList[vertexOne]) { 34 | for (let i = 0; i < this.adjacencyList[vertexOne].length; i++) { 35 | if (this.adjacencyList[vertexOne][i].value === vertexTwo) { 36 | this.adjacencyList[vertexOne].splice(i, 1); 37 | } 38 | } 39 | } 40 | 41 | if (this.adjacencyList[vertexTwo]) { 42 | for (let i = 0; i < this.adjacencyList[vertexTwo].length; i++) { 43 | if (this.adjacencyList[vertexTwo][i].value === vertexOne) { 44 | this.adjacencyList[vertexTwo].splice(i, 1); 45 | } 46 | } 47 | } 48 | } 49 | 50 | hasCycle() { 51 | const self = this; 52 | const visited = {}; 53 | const start = Object.keys(this.adjacencyList)[0]; 54 | 55 | function traverse(vertex, prevVertex) { 56 | visited[vertex] = true; 57 | 58 | for (const linkedVertex of self.adjacencyList[vertex]) { 59 | if (visited[linkedVertex.value] && linkedVertex.value !== prevVertex) return true; 60 | if (!visited[linkedVertex.value] && traverse(linkedVertex.value, vertex)) return true; 61 | } 62 | 63 | return false; 64 | } 65 | 66 | return traverse(start, null); 67 | } 68 | } 69 | 70 | const weightedGraph = new UndirectedGraph(); 71 | 72 | weightedGraph.addEdge('A', 'B', 7); 73 | weightedGraph.addEdge('A', 'C', 3); 74 | weightedGraph.addEdge('C', 'B', 1); 75 | weightedGraph.addEdge('C', 'D', 2); 76 | weightedGraph.addEdge('B', 'D', 2); 77 | weightedGraph.addEdge('B', 'E', 6); 78 | weightedGraph.addEdge('D', 'E', 4); 79 | weightedGraph.addEdge('C', 'F', 3); 80 | weightedGraph.addEdge('F', 'D', 4); 81 | weightedGraph.addEdge('F', 'B', 6); 82 | weightedGraph.addEdge('F', 'E', 3); 83 | weightedGraph.addEdge('A', 'D', 7); 84 | weightedGraph.addEdge('F', 'K', 4); 85 | weightedGraph.addEdge('K', 'L', 3); 86 | weightedGraph.addEdge('L', 'M', 5); 87 | weightedGraph.addEdge('M', 'N', 4); 88 | weightedGraph.addEdge('N', 'O', 2); 89 | weightedGraph.addEdge('O', 'P', 6); 90 | weightedGraph.addEdge('P', 'D', 1); 91 | 92 | console.log(weightedGraph.depthFirstSearchIterative('A')); 93 | // [ 'A', 'D', 'P', 'O', 'N', 'M', 'L', 'K', 'F', 'E', 'C', 'B' ] 94 | console.log(weightedGraph.depthFirstSearchRecursive('A')); 95 | // [ 'A', 'B', 'C', 'D', 'E', 'F', 'K', 'L', 'M', 'N', 'O', 'P' ] 96 | console.log(weightedGraph.breadthFirstSearchIterative('A')); 97 | // [ 'A', 'B', 'C', 'D', 'E', 'F', 'P', 'K', 'O', 'L', 'N', 'M' ] 98 | console.log(weightedGraph.findShortestDistance('A', 'M')); 99 | // { path: [ 'A', 'B', 'F', 'K', 'L', 'M' ], distance: 5 } 100 | console.log(weightedGraph.dijkstra('A', 'M')); 101 | // { path: [ 'A', 'C', 'F', 'K', 'L', 'M' ], distance: 18 } 102 | console.log(weightedGraph.hasCycle()); // true 103 | console.log(weightedGraph.travelingSalesmanProblemBF()); 104 | // { path: [ 'A', 'C', 'B', 'E', 'F', 'K', 'L', 'M', 'N', 'O', 'P', 'D', 'A' ], distance: 45 } 105 | console.log(weightedGraph.travelingSalesmanProblemDP()); 106 | // { path: [ 'A', 'C', 'B', 'E', 'F', 'K', 'L', 'M', 'N', 'O', 'P', 'D', 'A' ], distance: 45 } 107 | -------------------------------------------------------------------------------- /src/data-structures/hashtable/Hashtable-chaining-arrays.js: -------------------------------------------------------------------------------- 1 | // Hashtable - implementation using an array 2 | 3 | // Implement the following on the Hashtable class: 4 | 5 | // set 6 | // This function should accept a key and a value, hash the key, 7 | // store the key-value pair in the hash table array via separate chaining. 8 | 9 | // get 10 | // This function should accepts a key, retrieve the key-value pair in the hash table, 11 | // if the key isn't found, return undefined. 12 | 13 | // getKeys 14 | // This function should loop through the hash table array and return an array 15 | // of keys in the table. 16 | 17 | // getValues 18 | // This function should loop through the hash table array and return an array 19 | // of values in the table. 20 | 21 | // Additionally, the following method is implemented on the class: 22 | // delete - accepts a key, removes the key-value pair from the hash table 23 | 24 | class Hashtable { 25 | constructor (size = 53) { 26 | this.keyMap = new Array(size); 27 | } 28 | 29 | hash(key) { 30 | let hash = 0; 31 | const WEIRD_PRIME = 31; 32 | 33 | for (let i = 0; i < Math.min(key.length, 100); i++) { 34 | const char = key[i]; 35 | const value = char.charCodeAt(0) - 96; 36 | hash = (hash * WEIRD_PRIME + value) % this.keyMap.length; 37 | } 38 | 39 | return hash; 40 | } 41 | 42 | set(key, value) { 43 | const index = this.hash(key); 44 | 45 | if (!this.keyMap[index]) this.keyMap[index] = []; 46 | 47 | else { 48 | for (const item of this.keyMap[index]) { 49 | if (item[0] === key) { 50 | item[1] = value; 51 | return; 52 | } 53 | } 54 | } 55 | 56 | this.keyMap[index].push([key, value]); 57 | } 58 | 59 | get(key) { 60 | const index = this.hash(key); 61 | 62 | if (this.keyMap[index]) { 63 | for (const item of this.keyMap[index]) { 64 | if (item[0] === key) { 65 | return item[1]; 66 | } 67 | } 68 | } 69 | 70 | return undefined; 71 | } 72 | 73 | delete(key) { 74 | const index = this.hash(key); 75 | 76 | if (this.keyMap[index]) { 77 | for (let i = 0; i < this.keyMap[index].length; i++) { 78 | if (this.keyMap[index][i][0] === key) { 79 | return [].concat(...this.keyMap[index].splice(i, 1)); 80 | } 81 | } 82 | } 83 | 84 | return null; 85 | } 86 | 87 | getKeys() { 88 | const keys = []; 89 | 90 | for (const bucket of this.keyMap) { 91 | if (bucket) { 92 | for (const item of bucket) { 93 | keys.push(item[0]); 94 | } 95 | } 96 | } 97 | 98 | return keys; 99 | } 100 | 101 | getValues() { 102 | const values = new Set(); 103 | 104 | for (const bucket of this.keyMap) { 105 | if (bucket) { 106 | for (const item of bucket) { 107 | values.add(item[1]); 108 | } 109 | } 110 | } 111 | 112 | return Array.from(values); 113 | } 114 | 115 | print() { 116 | for (let i = 0; i < this.keyMap.length; i++) { 117 | if (this.keyMap[i]) { 118 | for (const item of this.keyMap[i]) { 119 | console.log(`bucket ${i}: ${item}`); 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | const hashtable = new Hashtable(17); 127 | 128 | hashtable.set('maroon', '#800000'); 129 | hashtable.set('yellow', '#FFFF00'); 130 | hashtable.set('olive', '#808000'); 131 | hashtable.set('salmon', '#FA8072'); 132 | hashtable.set('lightcoral', '#F08080'); 133 | hashtable.set('mediumvioletred', '#C71585'); 134 | hashtable.set('plum', '#DDA0DD'); 135 | hashtable.set('lightcoral', '#EE7F74'); 136 | hashtable.set('coral', '#EE7F74'); 137 | hashtable.print(); 138 | // bucket 0: plum,#DDA0DD 139 | // bucket 3: salmon,#FA8072 140 | // bucket 8: maroon,#800000 141 | // bucket 8: yellow,#FFFF00 142 | // bucket 9: coral,#EE7F74 143 | // bucket 10: olive,#808000 144 | // bucket 13: lightcoral,#EE7F74 145 | // bucket 16: mediumvioletred,#C71585 146 | console.log(hashtable.get('olive')); // #808000 147 | console.log(hashtable.get('oliv')); // undefined 148 | console.log(hashtable.getKeys()); // [ 'plum', 'salmon', 'maroon', 'yellow', 'coral', 'olive', 'lightcoral', 'mediumvioletred' ] 149 | console.log(hashtable.getValues()); // [ '#DDA0DD', '#FA8072', '#800000', '#FFFF00', '#EE7F74', '#808000', '#C71585' ] 150 | console.log(hashtable.delete('maroon')); // [ 'maroon', '#800000' ] 151 | console.log(hashtable.delete('maro')); // null 152 | hashtable.print(); 153 | // bucket 0: plum,#DDA0DD 154 | // bucket 3: salmon,#FA8072 155 | // bucket 8: yellow,#FFFF00 156 | // bucket 9: coral,#EE7F74 157 | // bucket 10: olive,#808000 158 | // bucket 13: lightcoral,#EE7F74 159 | // bucket 16: mediumvioletred,#C71585 160 | -------------------------------------------------------------------------------- /src/data-structures/hashtable/Hashtable-chaining-linked-lists.js: -------------------------------------------------------------------------------- 1 | // Hashtable - implementation using a singly linked list 2 | 3 | // Implement the following on the Hashtable class: 4 | 5 | // set 6 | // This function should accept a key and a value, hash the key, 7 | // store the key-value pair in the hash table array via separate chaining. 8 | 9 | // get 10 | // This function should accepts a key, retrieve the key-value pair in the hash table, 11 | // if the key isn't found, return undefined. 12 | 13 | // getKeys 14 | // This function should loop through the hash table array and return an array 15 | // of keys in the table. 16 | 17 | // getValues 18 | // This function should loop through the hash table array and return an array 19 | // of values in the table. 20 | 21 | // Additionally, the following method is implemented on the class: 22 | // delete - accepts a key, removes the key-value pair from the hash table 23 | 24 | const SinglyLinkedList = require('../singly-linked-list/Singly-linked-list'); 25 | 26 | class Hashtable { 27 | constructor (size = 53) { 28 | this.keyMap = new Array(size).fill(null).map(() => new SinglyLinkedList()); 29 | } 30 | 31 | hash(key) { 32 | let hash = 0; 33 | const WEIRD_PRIME = 31; 34 | 35 | for (let i = 0; i < Math.min(key.length, 100); i++) { 36 | const char = key[i]; 37 | const value = char.charCodeAt(0) - 96; 38 | hash = (hash * WEIRD_PRIME + value) % this.keyMap.length; 39 | } 40 | 41 | return hash; 42 | } 43 | 44 | set(key, value) { 45 | const bucketIndex = this.hash(key); 46 | const item = this.keyMap[bucketIndex].find((data) => data[0] === key); 47 | 48 | if (item) item.data[1] = value; 49 | else this.keyMap[bucketIndex].unshift([key, value]); 50 | } 51 | 52 | get(key) { 53 | const bucketIndex = this.hash(key); 54 | const item = this.keyMap[bucketIndex].find((data) => data[0] === key); 55 | 56 | return item ? item.data[1] : undefined; 57 | } 58 | 59 | delete(key) { 60 | const bucketIndex = this.hash(key); 61 | const itemIndex = this.keyMap[bucketIndex].find((data) => data[0] === key, true); 62 | 63 | if (itemIndex) return this.keyMap[bucketIndex].remove(itemIndex).data; 64 | 65 | return null; 66 | } 67 | 68 | getKeys() { 69 | let keys = []; 70 | 71 | for (const bucket of this.keyMap) { 72 | keys = keys.concat(bucket.iterate((data) => data[0])); 73 | } 74 | 75 | return keys; 76 | } 77 | 78 | getValues() { 79 | let values = []; 80 | 81 | for (const bucket of this.keyMap) { 82 | values = values.concat(bucket.iterate((data) => data[1])); 83 | } 84 | 85 | return Array.from(new Set(values)); 86 | } 87 | 88 | print() { 89 | for (let i = 0; i < this.keyMap.length; i++) { 90 | if (this.keyMap[i].head) console.log(`bucket ${i}: ${this.keyMap[i].iterate()}`); 91 | } 92 | } 93 | } 94 | 95 | const hashtable = new Hashtable(17); 96 | 97 | hashtable.set('maroon', '#800000'); 98 | hashtable.set('yellow', '#FFFF00'); 99 | hashtable.set('olive', '#808000'); 100 | hashtable.set('salmon', '#FA8072'); 101 | hashtable.set('lightcoral', '#F08080'); 102 | hashtable.set('mediumvioletred', '#C71585'); 103 | hashtable.set('plum', '#DDA0DD'); 104 | hashtable.set('lightcoral', '#EE7F74'); 105 | hashtable.set('coral', '#EE7F74'); 106 | hashtable.print(); 107 | // bucket 0: plum,#DDA0DD 108 | // bucket 3: salmon,#FA8072 109 | // bucket 8: yellow,#FFFF00,maroon,#800000 110 | // bucket 9: coral,#EE7F74 111 | // bucket 10: olive,#808000 112 | // bucket 13: lightcoral,#EE7F74 113 | // bucket 16: mediumvioletred,#C71585 114 | console.log(hashtable.get('olive')); // #808000 115 | console.log(hashtable.get('oliv')); // undefined 116 | console.log(hashtable.getKeys()); // [ 'plum','salmon', 'yellow', 'maroon', 'coral', 'olive', 'lightcoral', 'mediumvioletred' ] 117 | console.log(hashtable.getValues()); // [ '#DDA0DD', '#FA8072', '#FFFF00', '#800000', '#EE7F74', '#808000', '#C71585' ] 118 | console.log(hashtable.delete('maroon')); // [ 'maroon', '#800000' ] 119 | console.log(hashtable.delete('maro')); // null 120 | hashtable.print(); 121 | // bucket 0: plum,#DDA0DD 122 | // bucket 3: salmon,#FA8072 123 | // bucket 8: yellow,#FFFF00 124 | // bucket 9: coral,#EE7F74 125 | // bucket 10: olive,#808000 126 | // bucket 13: lightcoral,#EE7F74 127 | // bucket 16: mediumvioletred,#C71585 128 | -------------------------------------------------------------------------------- /src/data-structures/priority-queue/Priority-queue-node.js: -------------------------------------------------------------------------------- 1 | // Each Node has a value and a priority. 2 | 3 | module.exports = class PriorityQueueNode { 4 | constructor(value, priority) { 5 | this.value = value; 6 | this.priority = priority; 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/data-structures/priority-queue/Priority-queue.js: -------------------------------------------------------------------------------- 1 | // Priority Queue 2 | 3 | // Implement Priority Queue with Min Binary Heap 4 | // Each Node has a value and a priority. Use the priority to build the heap. 5 | 6 | // enqueue 7 | // This method accepts a value and priority, makes a new node, 8 | // and puts it in the right spot based off of its priority. 9 | 10 | // dequeue 11 | // This method removes root element, returns it, and rearranges heap using priority. 12 | 13 | // Additionally, the following method is implemented on the class: 14 | // changePriority - changes priority of node 15 | 16 | const PriorityQueueNode = require('./Priority-queue-node'); 17 | const MinBinaryHeap = require('../binary-heap/Min-binary-heap'); 18 | 19 | class PriorityQueue extends MinBinaryHeap { 20 | constructor() { 21 | super(); 22 | this.getValue = this.getValue.bind(this); 23 | } 24 | 25 | getItem(index) { 26 | return this.values[index].priority; 27 | } 28 | 29 | getValue(index) { 30 | return this.values[index].value; 31 | } 32 | 33 | enqueue(value, priority) { 34 | return super.insert(new PriorityQueueNode(value, priority)); 35 | } 36 | 37 | dequeue() { 38 | return super.extractMin(); 39 | } 40 | 41 | findByValue(value) { 42 | return super.find(value, this.getValue); 43 | } 44 | 45 | remove(value) { 46 | return super.remove(value, this.getValue); 47 | } 48 | 49 | changePriority(value, priority) { 50 | this.remove(value); 51 | return this.enqueue(value, priority); 52 | } 53 | 54 | printPriorityQueue() { 55 | for (const item of this.values) { 56 | console.log(`${item.value} - ${item.priority}`); 57 | } 58 | } 59 | } 60 | 61 | if (module.parent) { 62 | module.exports = PriorityQueue; 63 | } else { 64 | const priorityQueue = new PriorityQueue().enqueue('cat', 1).enqueue('dog', 2).enqueue('fish', 3).enqueue('rat', 4).enqueue('horse', 5).enqueue('squirrel', 6).enqueue('snake', 2); 65 | 66 | priorityQueue.printPriorityQueue(); // cat - 1, dog - 2, snake - 2, rat - 4, horse - 5, squirrel - 6, fish - 3 67 | console.log(priorityQueue.findByValue('horse')); // [ 4 ] 68 | priorityQueue.remove('fish'); 69 | priorityQueue.printPriorityQueue(); // cat - 1, dog - 2, snake - 2, rat - 4, horse - 5, squirrel - 6 70 | priorityQueue.enqueue('crow', 4).enqueue('rabbit', 3); 71 | priorityQueue.printPriorityQueue(); // cat - 1, dog - 2, snake - 2, rabbit - 3, horse - 5, squirrel - 6, crow - 4, rat - 4 72 | console.log(priorityQueue.dequeue()); 73 | priorityQueue.printPriorityQueue(); // dog - 2, rabbit - 3, snake - 2, rat - 4, horse - 5, squirrel - 6, crow - 4 74 | } 75 | -------------------------------------------------------------------------------- /src/data-structures/queue/Queue-from-stack.js: -------------------------------------------------------------------------------- 1 | // Queue with 2 stacks 2 | 3 | // Implement the following operations of a queue using two stacks: 4 | // - enqueue (returns the queue) 5 | // - dequeue (returns the dequeue value) 6 | 7 | // Make sure to write your time and space complexities for each function 8 | 9 | const Stack = require('../stack/Stack'); 10 | 11 | class Queue { 12 | constructor() { 13 | this.main = new Stack(); 14 | this.helper = new Stack(); 15 | } 16 | 17 | // Time complexity - O(1), space complexity - O(1) 18 | enqueue(data) { 19 | this.main.push(data); 20 | 21 | return this; 22 | } 23 | 24 | // Time complexity - O(n), space complexity - O(n) 25 | dequeue() { 26 | while (this.main.size > 1) { 27 | this.helper.push(this.main.pop()); 28 | } 29 | 30 | const removedNode = this.main.pop(); 31 | 32 | while (this.helper.size > 0) { 33 | this.main.push(this.helper.pop()); 34 | } 35 | 36 | return removedNode; 37 | } 38 | } 39 | 40 | const q1 = new Queue(); 41 | console.log(q1.enqueue(3)); // returns the queue 42 | console.log(q1.enqueue(4)); // returns the queue 43 | console.log(q1.enqueue(5)); // returns the queue 44 | console.log(q1.enqueue(6).enqueue(7)); // returns the queue 45 | console.log(q1.dequeue()); // 3 46 | console.log(q1.dequeue()); // 4 47 | console.log(q1.dequeue()); // 5 48 | console.log(q1.dequeue()); // 6 49 | console.log(q1.dequeue()); // 7 50 | console.log(q1.dequeue()); // null 51 | -------------------------------------------------------------------------------- /src/data-structures/queue/Queue-node.js: -------------------------------------------------------------------------------- 1 | module.exports = class QueueNode { 2 | constructor(data, next = null) { 3 | this.data = data; 4 | this.next = next; 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /src/data-structures/queue/Queue.js: -------------------------------------------------------------------------------- 1 | // Queue 2 | 3 | // Implement the following methods on the Queue class. 4 | // 5 | // enqueue 6 | // This function adds the value to the end of the queue. 7 | // This should be an O(1) operation and return the new size of the queue. 8 | 9 | // dequeue 10 | // This function removes the value at the beginning of the queue. 11 | // This should be an O(1) operation and return the value removed. 12 | 13 | const QueueNode = require('./Queue-node'); 14 | 15 | class Queue { 16 | constructor () { 17 | this.first = null; 18 | this.last = null; 19 | this.size = 0; 20 | } 21 | 22 | enqueue(data) { 23 | const newNode = new QueueNode(data); 24 | 25 | if (!this.first) this.first = newNode; 26 | else this.last.next = newNode; 27 | 28 | this.last = newNode; 29 | this.size++; 30 | 31 | return this.size; 32 | } 33 | 34 | dequeue() { 35 | if (!this.first) return null; 36 | 37 | const removedNode = this.first; 38 | this.first = removedNode.next; 39 | this.size--; 40 | 41 | if (!this.size) this.last = null; 42 | 43 | return removedNode.data; 44 | } 45 | } 46 | 47 | if (module.parent) { 48 | module.exports = Queue; 49 | } else { 50 | const queue = new Queue(); 51 | 52 | console.log(queue.enqueue(10)); // 1 53 | console.log(queue.size); // 1 54 | console.log(queue.enqueue(100)); // 2 55 | console.log(queue.size); // 2 56 | console.log(queue.enqueue(1000)); // 3 57 | console.log(queue.size); // 3 58 | console.log(queue.dequeue()); // 10 59 | queue.dequeue(); 60 | console.log(queue.size); // 1 61 | queue.dequeue(); 62 | console.log(queue.dequeue()); // null 63 | console.log(queue.size); // 1 64 | } 65 | -------------------------------------------------------------------------------- /src/data-structures/singly-linked-list/Singly-linked-list-node.js: -------------------------------------------------------------------------------- 1 | module.exports = class SinglyLinkedListNode { 2 | constructor(data, next = null) { 3 | this.data = data; 4 | this.next = next; 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /src/data-structures/stack/Stack-form-queue.js: -------------------------------------------------------------------------------- 1 | // Stack with 2 Queues 2 | 3 | // Implement a stack using two queues: 4 | // - push (returns the stack) 5 | // - pop (returns the value popped) 6 | 7 | // Comment on your time complexity for all of these operations. 8 | 9 | const Queue = require('../queue/Queue'); 10 | 11 | class Stack { 12 | constructor() { 13 | this.main = new Queue(); 14 | this.helper = new Queue(); 15 | } 16 | 17 | // Time complexity - O(n), space complexity - O(n) 18 | push(data) { 19 | while (this.main.size > 0) { 20 | this.helper.enqueue(this.main.dequeue()); 21 | } 22 | 23 | this.main.enqueue(data); 24 | 25 | while (this.helper.size > 0) { 26 | this.main.enqueue(this.helper.dequeue()); 27 | } 28 | 29 | return this; 30 | } 31 | 32 | // Time complexity - O(1), space complexity - O(1) 33 | pop() { 34 | return this.main.dequeue(); 35 | } 36 | } 37 | 38 | const stack = new Stack(); 39 | 40 | stack.push(10).push(20).push(30); 41 | console.log(stack.pop()); // 30 42 | console.log(stack.pop()); // 20 43 | console.log(stack.pop()); // 10 44 | console.log(stack.pop()); // null 45 | stack.push(30).push(40).push(50); 46 | console.log(stack.pop()); // 50 47 | stack.push(60); 48 | console.log(stack.pop()); // 60 49 | -------------------------------------------------------------------------------- /src/data-structures/stack/Stack-node.js: -------------------------------------------------------------------------------- 1 | module.exports = class StackNode { 2 | constructor(data, next = null) { 3 | this.data = data; 4 | this.next = next; 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /src/data-structures/stack/Stack.js: -------------------------------------------------------------------------------- 1 | // Stack 2 | 3 | // Implement the following methods on the Stack class: 4 | 5 | // push - takes in a node and puts it at the top of the stack. 6 | // Should return the new size of the stack. 7 | 8 | // pop - removes the node at the top of the stack and returns the value of that node. 9 | 10 | const StackNode = require('./Stack-node'); 11 | 12 | class Stack { 13 | constructor() { 14 | this.first = null; 15 | this.last = null; 16 | this.size = 0; 17 | } 18 | 19 | push(data) { 20 | this.first = new StackNode(data, this.first); 21 | 22 | if (!this.size) this.last = this.first; 23 | this.size++; 24 | 25 | return this.size; 26 | } 27 | 28 | pop() { 29 | if (!this.first) return null; 30 | 31 | const removedNode = this.first; 32 | this.first = removedNode.next; 33 | this.size--; 34 | 35 | if (!this.size) this.last = null; 36 | 37 | return removedNode.data; 38 | } 39 | } 40 | 41 | if (module.parent) { 42 | module.exports = Stack; 43 | } else { 44 | const stack = new Stack(); 45 | 46 | stack.push(10); 47 | stack.push(100); 48 | stack.push(1000); 49 | console.log(stack.pop()); // 1000 50 | console.log(stack.size); // 2 51 | stack.pop(); 52 | stack.pop(); 53 | console.log(stack.size); // 0 54 | console.log(stack.pop()); // null 55 | } 56 | -------------------------------------------------------------------------------- /src/data-structures/trie/Trie-node.js: -------------------------------------------------------------------------------- 1 | class TrieNode { 2 | constructor(character, isWord = false) { 3 | this.character = character; 4 | this.characters = {}; 5 | this.isWord = isWord; 6 | } 7 | 8 | getSubNode(char) { 9 | return this.characters[char]; 10 | } 11 | 12 | addSubNode(char, isWord = false) { 13 | if (!this.getSubNode(char)) this.characters[char] = new TrieNode(char, isWord); 14 | else this.characters[char].isWord = this.characters[char].isWord || isWord; 15 | 16 | return this.characters[char]; 17 | } 18 | 19 | removeSubNode(char) { 20 | const subNode = this.getSubNode(char); 21 | 22 | if (subNode && !subNode.isWord && !subNode.hasSubNodes()) { 23 | delete this.characters[char]; 24 | return subNode; 25 | } 26 | 27 | return false; 28 | } 29 | 30 | hasSubNodes() { 31 | return !!Object.keys(this.characters).length; 32 | } 33 | } 34 | 35 | module.exports = TrieNode; 36 | -------------------------------------------------------------------------------- /src/data-structures/trie/Trie-without-trie-node.js: -------------------------------------------------------------------------------- 1 | // Trie 2 | 3 | // Implement the following on the Trie class 4 | 5 | // addWord 6 | // This function should add the given word starting from the given index to the Trie. 7 | // It will be recursive and notify the correct child of this Trie to add the word 8 | // starting from a later index. Consider what the add function should do 9 | // when it reaches the end of the word as a word does not necessarily end at a leaf. 10 | // You must mark nodes which are the ends of words so that the words can be reconstructed later. 11 | 12 | // getWords 13 | // This function should return an array of all of the words in the Trie. 14 | 15 | // findWord 16 | // This function should accept a string and return the characters object 17 | // for the last character in that word if the string is a word in the Trie, 18 | // otherwise - undefined. Try to solve this without having to find every single word in the Trie. 19 | 20 | // removeWord 21 | // This function should accept a string and remove the word from the Trie. 22 | 23 | // autocomplete 24 | // This function should accept a string and return an array of all the possible 25 | // options in the trie for that string. 26 | 27 | class Trie { 28 | constructor(isWord = false) { 29 | this.characters = {}; 30 | this.isWord = isWord; 31 | } 32 | 33 | addWord(word, index = 0) { 34 | if (word.length === index) return this; 35 | 36 | const char = word[index]; 37 | let subTrie; 38 | 39 | if (this.characters[char]) { 40 | subTrie = this.characters[char]; 41 | subTrie.isWord = subTrie.isWord || word.length - 1 === index; 42 | } else { 43 | subTrie = new Trie(word.length - 1 === index); 44 | this.characters[char] = subTrie; 45 | } 46 | 47 | subTrie.addWord(word, index + 1); 48 | 49 | return this; 50 | } 51 | 52 | removeWord(word) { 53 | this._removeWord(word); 54 | 55 | return this; 56 | } 57 | 58 | _removeWord(word, index = 0) { 59 | const char = word[index]; 60 | const subTrie = this.characters[char]; 61 | 62 | if (!subTrie) return false; 63 | 64 | if (word.length - 1 > index) { 65 | return subTrie._removeWord(word, index + 1) && 66 | !subTrie.isWord && !Object.keys(subTrie.characters).length 67 | ? delete this.characters[char] 68 | : false; 69 | } 70 | 71 | subTrie.isWord = false; 72 | 73 | return !Object.keys(subTrie.characters).length ? delete this.characters[char] : false; 74 | } 75 | 76 | getWords(currentWord = '', words = []) { 77 | for (const char in this.characters) { 78 | if (Object.prototype.hasOwnProperty.call(this.characters, char)) { 79 | const nextWord = currentWord + char; 80 | const subTrie = this.characters[char]; 81 | 82 | if (subTrie.isWord) words.push(nextWord); 83 | 84 | subTrie.getWords(nextWord, words); 85 | } 86 | } 87 | 88 | return words; 89 | } 90 | 91 | countWords() { 92 | return this.getWords().length; 93 | } 94 | 95 | findSequence(word, index = 0) { 96 | const char = word[index]; 97 | const subTrie = this.characters[char]; 98 | 99 | if (word.length - 1 > index && subTrie) return subTrie.findSequence(word, index + 1); 100 | 101 | return subTrie; 102 | } 103 | 104 | findWord(word) { 105 | const lastChar = this.findSequence(word); 106 | 107 | return lastChar && lastChar.isWord ? lastChar : undefined; 108 | } 109 | 110 | autoComplete(prefix) { 111 | const subTrie = this.findSequence(prefix); 112 | 113 | return subTrie ? subTrie.getWords(prefix) : []; 114 | } 115 | 116 | print() { 117 | console.log(this.getWords()); 118 | } 119 | } 120 | 121 | const trie = new Trie(); 122 | 123 | trie.addWord('fun'); 124 | trie.addWord('fast'); 125 | trie.addWord('fat'); 126 | trie.addWord('fate'); 127 | trie.addWord('father'); 128 | trie.addWord('forget'); 129 | trie.addWord('awesome'); 130 | trie.addWord('argue'); 131 | 132 | console.log(trie.getWords()); // ["fun", "fast", "fat", "fate", "father", "forget", "awesome", "argue"] 133 | console.log(trie.countWords()); // 8 134 | console.log(trie.findWord('fat').isWord); // true 135 | trie.removeWord('fat'); 136 | console.log(trie.findWord('fat')); // undefined 137 | console.log(trie.findSequence('fat').isWord); // false 138 | trie.removeWord('argue'); 139 | trie.print(); // [ 'fun', 'fast', 'fate', 'father', 'forget', 'awesome' ] 140 | console.log(trie.autoComplete('fa')); // [ 'fast', 'fate', 'father' ] 141 | -------------------------------------------------------------------------------- /src/data-structures/trie/Trie.js: -------------------------------------------------------------------------------- 1 | // Trie - implementation using TrieNode class 2 | 3 | // Implement the following on the Trie class 4 | 5 | // addWord 6 | // This function should add the given word starting from the given index to the Trie. 7 | // It will be recursive and notify the correct child of this Trie to add the word 8 | // starting from a later index. Consider what the add function should do 9 | // when it reaches the end of the word as a word does not necessarily end at a leaf. 10 | // You must mark nodes which are the ends of words so that the words can be reconstructed later. 11 | 12 | // getWords 13 | // This function should return an array of all of the words in the Trie. 14 | 15 | // findWord 16 | // This function should accept a string and return the characters object 17 | // for the last character in that word if the string is a word in the Trie, 18 | // otherwise - undefined. Try to solve this without having to find every single word in the Trie. 19 | 20 | // removeWord 21 | // This function should accept a string and remove the word from the Trie. 22 | 23 | // autocomplete 24 | // This function should accept a string and return an array of all the possible 25 | // options in the trie for that string. 26 | 27 | const TrieNode = require('./Trie-node'); 28 | 29 | class Trie { 30 | constructor() { 31 | this.root = new TrieNode(''); 32 | } 33 | 34 | addWord(word) { 35 | let currentNode = this.root; 36 | 37 | for (let i = 0; i < word.length; i++) { 38 | currentNode = currentNode.addSubNode(word[i], i === word.length - 1); 39 | } 40 | 41 | return this; 42 | } 43 | 44 | removeWord(word) { 45 | this._removeWord(word); 46 | 47 | return this; 48 | } 49 | 50 | _removeWord(word, node = this.root, index = 0) { 51 | const nextNode = node.getSubNode(word[index]); 52 | 53 | if (!nextNode) return false; 54 | 55 | if (word.length - 1 > index) { 56 | return this._removeWord(word, nextNode, index + 1) && node.removeSubNode(word[index]); 57 | } 58 | 59 | nextNode.isWord = false; 60 | 61 | return node.removeSubNode(word[index]); 62 | } 63 | 64 | getWords() { 65 | return this._getWords(); 66 | } 67 | 68 | _getWords(node = this.root, currentWord = '', words = []) { 69 | for (const char in node.characters) { 70 | if (Object.prototype.hasOwnProperty.call(node.characters, char)) { 71 | const nextWord = currentWord + char; 72 | const subNode = node.getSubNode(char); 73 | 74 | if (subNode.isWord) words.push(nextWord); 75 | 76 | this._getWords(subNode, nextWord, words); 77 | } 78 | } 79 | 80 | return words; 81 | } 82 | 83 | countWords() { 84 | return this._getWords().length; 85 | } 86 | 87 | findSequence(word) { 88 | let currentNode = this.root; 89 | 90 | for (let i = 0; i < word.length; i++) { 91 | if (!currentNode) return currentNode; 92 | currentNode = currentNode.getSubNode(word[i]); 93 | } 94 | 95 | return currentNode; 96 | } 97 | 98 | findWord(word) { 99 | const lastChar = this.findSequence(word); 100 | 101 | return lastChar && lastChar.isWord ? lastChar : undefined; 102 | } 103 | 104 | autoComplete(prefix) { 105 | const subNode = this.findSequence(prefix); 106 | 107 | return subNode ? this._getWords(subNode, prefix) : []; 108 | } 109 | 110 | print() { 111 | console.log(this.getWords()); 112 | } 113 | } 114 | 115 | const trie = new Trie(); 116 | 117 | trie.addWord('fun'); 118 | trie.addWord('fast'); 119 | trie.addWord('fat'); 120 | trie.addWord('fate'); 121 | trie.addWord('father'); 122 | trie.addWord('forget'); 123 | trie.addWord('awesome'); 124 | trie.addWord('argue'); 125 | 126 | console.log(trie.getWords()); // ["fun", "fast", "fat", "fate", "father", "forget", "awesome", "argue"] 127 | console.log(trie.countWords()); // 8 128 | console.log(trie.findWord('fat').character); // t 129 | trie.removeWord('fat'); 130 | console.log(trie.findWord('fat')); // undefined 131 | console.log(trie.findSequence('fat').character); // t 132 | trie.removeWord('argue'); 133 | trie.print(); // [ 'fun', 'fast', 'fate', 'father', 'forget', 'awesome' ] 134 | console.log(trie.autoComplete('fa')); // [ 'fast', 'fate', 'father' ] 135 | -------------------------------------------------------------------------------- /src/dynamic-programming/coin-change.js: -------------------------------------------------------------------------------- 1 | // Write a function called coinChange which accepts two parameters: 2 | // an array of denominations and a value. The function should return the number 3 | // of ways you can obtain the value from the given collection of denominations. 4 | // You can think of this as figuring out the number of ways to make change 5 | // for a given value from a supply of coins. 6 | // Use Dynamic Programming approach. 7 | 8 | // Brute Force 9 | // Time Complexity exponential 10 | // Space Complexity O(1) 11 | function coinChangeBF(denominations, value) { 12 | const index = denominations.length - 1; 13 | 14 | function change(denominations, value, index) { 15 | if (value < 0 || index < 0) return 0; 16 | if (value === 0) return 1; 17 | 18 | return change(denominations, value - denominations[index], index) + 19 | change(denominations, value, index - 1); 20 | } 21 | 22 | return change(denominations, value, index); 23 | } 24 | 25 | console.log(coinChangeBF([1, 5, 10, 25], 100)); // 242 26 | 27 | // Dynamic Programming 28 | 29 | // Top-down approach - Memoization 30 | // Time Complexity O(value * denominations.length) 31 | // Space Complexity O(value * denominations.length) 32 | function coinChangeTD(denominations, value) { 33 | const index = denominations.length - 1; 34 | const memo = {}; 35 | 36 | function change(denominations, value, index) { 37 | if (value < 0 || index < 0) return 0; 38 | if (value === 0) return 1; 39 | 40 | const key = `${index}/${value}`; 41 | 42 | if (memo[key]) return memo[key]; 43 | 44 | memo[key] = change(denominations, value - denominations[index], index) + 45 | change(denominations, value, index - 1); 46 | 47 | return memo[key]; 48 | } 49 | 50 | return change(denominations, value, index); 51 | } 52 | 53 | console.log(coinChangeTD([1, 5, 10, 25], 14511)); // 409222339 54 | 55 | // Bottom-up approach - Tabulation 56 | // Time Complexity Complexity O(value * denominations.length) 57 | // Space Complexity O(value) 58 | function coinChangeBU(denominations, value) { 59 | const table = Array.from({ length: value + 1 }).fill(0); 60 | table[0] = 1; 61 | 62 | for (let i = 0; i < denominations.length; i++) { 63 | for (let j = denominations[i]; j <= value; j++) { 64 | table[j] += table[j - denominations[i]]; 65 | } 66 | } 67 | 68 | return table[value]; 69 | } 70 | 71 | console.log(coinChangeBU([1, 5, 10, 25], 14511)); // 409222339 72 | -------------------------------------------------------------------------------- /src/dynamic-programming/definition.md: -------------------------------------------------------------------------------- 1 | ## What is Dynamic Programming? 2 | 3 | A method for solving a complex problem by breaking it down into a collection of simpler subproblems, solving each of those subproblems just once, and storing their solutions. 4 | 5 | ### Overlapping Subproblems 6 | A problem is said to have overlapping subproblems if it can be broken down into subproblems which are reused several times. 7 | 8 | ### Memoization 9 | Storing the results of expensive function calls and returning the cached result when the same inputs occur again. 10 | 11 | 12 | ### Tabulation 13 | Storing the result of a previous result in a "table" (usually an array). 14 | 15 | Usually done using iteration. 16 | 17 | Better space complexity can be achieved using tabulation. 18 | 19 | ## Lecture Node 20 | 21 | https://cs.slides.com/colt_steele/dynamic-programming#/ 22 | -------------------------------------------------------------------------------- /src/dynamic-programming/fibonacci.js: -------------------------------------------------------------------------------- 1 | // fib 2 | // Write a recursive function called fib which accepts a number 3 | // and returns the nth number in the Fibonacci sequence using Dynamic Programming approach. 4 | // Recall that the Fibonacci sequence is the sequence of whole numbers 1, 1, 2, 3, 5, 8, ... 5 | // which starts with 1 and 1, and where every number thereafter 6 | // is equal to the sum of the previous two numbers. 7 | 8 | // Brute Force 9 | // Time Complexity O(2^n) 10 | function fibBF(num) { 11 | if (num < 2) return num; 12 | 13 | return fibBF(num - 1) + fibBF(num - 2); 14 | } 15 | 16 | console.log(fibBF(10)); // 55 17 | 18 | // Dynamic Programming 19 | // Time Complexity O(n) 20 | // Space Complexity O(n) 21 | 22 | // Top-down approach - Memoization 23 | function fibTD(num, cache = {}) { // or set default param as cache = [undefined, 1, 1] 24 | if (typeof cache[num] !== 'undefined') return cache[num]; 25 | if (num < 2) return num; 26 | 27 | cache[num] = fibTD(num - 1, cache) + fibTD(num - 2, cache); 28 | // console.log(memo) 29 | return cache[num]; 30 | } 31 | 32 | console.log(fibTD(1000)); // 4.346655768693743e+208 33 | // console.log(fibTD(10000)); // RangeError: Maximum call stack size exceeded 34 | 35 | // With a memoization function (does memoization between calls) 36 | function memoize(fn) { 37 | const cache = {}; 38 | 39 | return function(arg) { 40 | if (typeof cache[arg] !== 'undefined') return cache[arg]; 41 | 42 | cache[arg] = fn.call(this, arg); 43 | 44 | return cache[arg]; 45 | }; 46 | } 47 | 48 | const memFib = memoize(fibTDFunc); 49 | 50 | function fibTDFunc(num) { 51 | if (num < 2) return num; 52 | 53 | return memFib(num - 1) + memFib(num - 2); 54 | } 55 | 56 | console.log(fibTDFunc(1000)); // 4.346655768693743e+208 57 | // console.log(fibTDFunc(10000)); // RangeError: Maximum call stack size exceeded 58 | 59 | // Bottom-up approach - Tabulation 60 | // Space Complexity O(1) 61 | function fib_table(n) { 62 | if (n <= 2) return 1; 63 | let fibNums = [0, 1, 1]; 64 | for (let i = 3; i <= n; i++) { 65 | fibNums[i] = fibNums[n - 1] + fibNums[n - 2]; 66 | } 67 | return fibNums[n]; 68 | } 69 | 70 | // Nkaty's solution 71 | function fibBU(num) { 72 | if (num < 2) return num; 73 | 74 | let prevNumber = 0; 75 | let currentNumber = 1; 76 | let temp; 77 | 78 | for (let i = 1; i < num; i++) { 79 | temp = currentNumber; 80 | currentNumber += prevNumber; 81 | prevNumber = temp; 82 | } 83 | 84 | return currentNumber; 85 | } 86 | 87 | console.log(fibBU(1000)); // 4.346655768693743e+208 88 | console.log(fibBU(10000)); // Infinity 89 | 90 | // returns a fibonacci sequence as an array 91 | function fibBUArray(num) { 92 | const numbers = [1]; 93 | 94 | for (let i = 1; i < num; i++) { 95 | numbers[i] = numbers[i - 1] + (numbers[i - 2] || 0); 96 | } 97 | 98 | return numbers; 99 | } 100 | 101 | console.log(fibBUArray(10)); // [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ] 102 | -------------------------------------------------------------------------------- /src/dynamic-programming/maximum-subarray.js: -------------------------------------------------------------------------------- 1 | // Given an integer array nums, find the contiguous subarray 2 | // (containing at least one number) which has the largest sum and return its sum. 3 | 4 | // Time Complexity O(n) 5 | // Space Complexity O(n) 6 | function maxSubArray(nums) { 7 | const subArraySum = [nums[0]]; 8 | 9 | for (let i = 1; i < nums.length; i++) { 10 | subArraySum.push(Math.max(nums[i] + subArraySum[i - 1], nums[i])); 11 | } 12 | 13 | return Math.max(...subArraySum); 14 | }; 15 | 16 | console.log(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])); // 6 17 | console.log(maxSubArray([-1, 0, -2])); // 0 18 | console.log(maxSubArray([-1])); // -1 19 | 20 | // Time Complexity O(n) 21 | // Space Complexity O(1) 22 | function maxSubArrayWithoutAdditinalSpace(nums) { 23 | for (let i = 1; i < nums.length; i++) { 24 | nums[i] = Math.max(nums[i] + nums[i - 1], nums[i]); 25 | } 26 | 27 | return Math.max(...nums); 28 | }; 29 | 30 | console.log(maxSubArrayWithoutAdditinalSpace([-2, 1, -3, 4, -1, 2, 1, -5, 4])); // 6 31 | console.log(maxSubArrayWithoutAdditinalSpace([-1, 0, -2])); // 0 32 | console.log(maxSubArrayWithoutAdditinalSpace([-1])); // -1 33 | -------------------------------------------------------------------------------- /src/dynamic-programming/min-coin-change.js: -------------------------------------------------------------------------------- 1 | // Write a function called minCoinChange which accepts two parameters: 2 | // an array of coins and a amount. The function should return 3 | // the minimum number of coins needed to make the change. 4 | // Use Dynamic Programming approach and greedy algorithm. 5 | 6 | // Brute Force 7 | // Time Complexity exponential 8 | function minCoinChangeBF(coins, amount) { 9 | if (amount === 0) return 0; 10 | 11 | let result = Infinity; 12 | 13 | for (const coin of coins) { 14 | if (coin <= amount) { 15 | const currentResult = minCoinChangeBF(coins, amount - coin); 16 | 17 | if (currentResult !== Infinity) result = Math.min(result, currentResult + 1); 18 | } 19 | } 20 | 21 | return result; 22 | } 23 | 24 | console.log(minCoinChangeBF([9, 6, 5, 1], 12)); // 2 25 | console.log(minCoinChangeBF([10, 25], 85)); // 4 26 | 27 | // Dynamic Programming 28 | 29 | // Top-down approach - Memoization 30 | // Time Complexity O(amount * coins.length) 31 | // Space Complexity O(amount * coins.length) 32 | function minCoinChangeTD(coins, amount, memo = {}) { 33 | if (amount === 0) return 0; 34 | 35 | if (memo[amount]) return memo[amount]; 36 | 37 | let result = Infinity; 38 | 39 | for (const coin of coins) { 40 | if (coin <= amount) { 41 | const currentResult = minCoinChangeTD(coins, amount - coin, memo); 42 | 43 | if (currentResult !== Infinity) result = Math.min(result, currentResult + 1); 44 | } 45 | } 46 | 47 | memo[amount] = result; 48 | 49 | return result; 50 | } 51 | console.log(minCoinChangeTD([9, 6, 5, 1], 20000)); // 2223 52 | console.log(minCoinChangeTD([10, 25], 85)); // 4 53 | 54 | // Bottom-up approach - Tabulation 55 | // Time Complexity O(amount * coins.length) 56 | // Space Complexity O(amount * coins.length) 57 | function minCoinChangeBU (coins, amount) { 58 | const count = Array.from({ length: amount + 1 }).fill(Infinity); 59 | const usedCoins = Array.from({ length: amount + 1 }).fill(-1); 60 | count[0] = 0; 61 | 62 | for (let i = 0; i < coins.length; i++) { 63 | for (let j = coins[i]; j <= amount; j++) { 64 | if (count[j] > count[j - coins[i]] + 1) { 65 | count[j] = count[j - coins[i]] + 1; 66 | usedCoins[j] = coins[i]; 67 | } 68 | } 69 | } 70 | 71 | if (count[amount] === Infinity) return { coins: [], minNumberOfCoins: null }; 72 | 73 | const coinsResult = []; 74 | let currentIndex = amount; 75 | 76 | while (currentIndex > 0 && usedCoins[currentIndex] !== -1) { 77 | coinsResult.push(usedCoins[currentIndex]); 78 | currentIndex = currentIndex - usedCoins[currentIndex]; 79 | } 80 | 81 | return { coins: coinsResult, minNumberOfCoins: count[amount] }; 82 | } 83 | 84 | console.log(minCoinChangeBU([10, 25], 135)); // { coins: [ 25, 25, 25, 25, 25, 10 ], minNumberOfCoins: 6 } 85 | console.log(minCoinChangeBU([1, 5, 6, 9], 11)); // { coins: [ 6, 5 ], minNumberOfCoins: 2 } 86 | 87 | // greedy algorithm 88 | // Time Complexity O(amount) 89 | function minCoinChangeGA(coins, amount) { 90 | const result = []; 91 | coins.sort((a, b) => a - b); 92 | 93 | for (let i = coins.length - 1; i >= 0; i--) { 94 | while (amount >= coins[i]) { 95 | result.push(coins[i]); 96 | amount -= coins[i]; 97 | } 98 | } 99 | 100 | if (amount) return null; 101 | return result; 102 | } 103 | 104 | console.log(minCoinChangeGA([10, 25], 85)); // [ 25, 25, 25, 10 ] 105 | console.log(minCoinChangeGA([1, 2, 5, 10, 20, 50, 100], 70)); // [ 50, 20 ] 106 | 107 | // greedy algorithm does not always work properly 108 | console.log(minCoinChangeGA([1, 5, 6, 9], 11)); // [ 9, 1, 1 ] - instead of [6, 5] 109 | -------------------------------------------------------------------------------- /src/dynamic-programming/stairs.js: -------------------------------------------------------------------------------- 1 | // Write a function called stairs which accepts n number of stairs. 2 | // Imagine that a person is standing at the bottom of the stairs and wants 3 | // to reach the top and the person can climb either 1 stair or 2 stairs at a time. 4 | // Your function should return the number of ways the person can reach the top 5 | // by only climbing 1 or 2 stairs at a time. 6 | // Use Dynamic Programming approach. 7 | 8 | // Brute Force 9 | // Time Complexity O(2^n) 10 | function stairsBF(num) { 11 | if (num < 3) return num; 12 | return stairsBF(num - 1) + stairsBF(num - 2); 13 | } 14 | 15 | console.log(stairsBF(7)); // 21 16 | 17 | // Dynamic Programming 18 | // Time Complexity O(n) 19 | 20 | // Top-down approach - Memoization 21 | function stairsTD(num, cache = {}) { 22 | if (typeof cache[num] !== 'undefined') return cache[num]; 23 | if (num < 3) return num; 24 | 25 | cache[num] = stairsTD(num - 1, cache) + stairsTD(num - 2, cache); 26 | 27 | return cache[num]; 28 | } 29 | 30 | console.log(stairsTD(7)); // 21 31 | 32 | // With a memoization function (does memoization between calls) 33 | function memoize(fn) { 34 | const cache = {}; 35 | 36 | return function(arg) { 37 | if (typeof cache[arg] !== 'undefined') return cache[arg]; 38 | 39 | cache[arg] = fn.call(this, arg); 40 | 41 | return cache[arg]; 42 | }; 43 | } 44 | 45 | const memStairs = memoize(stairsTDFunc); 46 | 47 | function stairsTDFunc(num) { 48 | if (num < 3) return num; 49 | 50 | return memStairs(num - 1) + memStairs(num - 2); 51 | } 52 | 53 | console.log(stairsTDFunc(7)); // 21 54 | 55 | // Bottom-up approach - Tabulation 56 | function stairsBU(num) { 57 | if (num < 3) return num; 58 | 59 | let prevNumber = 1; 60 | let currentNumber = 1; 61 | let temp; 62 | 63 | for (let i = 2; i <= num; i++) { 64 | temp = currentNumber; 65 | currentNumber += prevNumber; 66 | prevNumber = temp; 67 | } 68 | 69 | return currentNumber; 70 | } 71 | 72 | console.log(stairsBU(7)); // 21 73 | -------------------------------------------------------------------------------- /src/other-algorithms/factorial-of-large-number.js: -------------------------------------------------------------------------------- 1 | function factorial(num) { 2 | if (num < 2) return 1; 3 | 4 | const result = num.toString().split('').reverse().map(Number); 5 | 6 | while (--num) { 7 | for (let carry = 0, i = 0; i < result.length || carry; i++) { 8 | const prod = (result[i] || 0) * num + carry; 9 | result[i] = prod % 10; 10 | carry = parseInt(prod / 10, 10); 11 | } 12 | } 13 | 14 | return result.reverse().join(''); 15 | } 16 | 17 | console.log(factorial(10000)); 18 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/char_counter.js: -------------------------------------------------------------------------------- 1 | function charCount1(str) { 2 | var obj = {}; 3 | for (var i = 0; i < str.length; i++) { 4 | var char = str[i].toLowerCase(); 5 | if (/[a-z0-9]/.test(char)) { 6 | if (obj[char] > 0) { 7 | obj[char]++; 8 | } else { 9 | obj[char] = 1; 10 | }; 11 | } 12 | } 13 | return obj; 14 | } 15 | 16 | function charCount2(str) { 17 | var obj = {}; 18 | for (var char of str) { 19 | char = char.toLowerCase(); 20 | if (/[a-z0-9]/.test(char)) { 21 | obj[char] = ++obj[char] || 1; 22 | } 23 | } 24 | return obj; 25 | } 26 | 27 | function charCount3(str) { 28 | var obj = {}; 29 | for (var char of str) { 30 | char = char.toLowerCase(); 31 | if (isAlphaNumeric(char)) { 32 | char = char.toLowerCase(); 33 | obj[char] = ++obj[char] || 1; 34 | } 35 | } 36 | return obj; 37 | } 38 | 39 | function isAlphaNumeric(char) { 40 | var code = char.charCodeAt(0) 41 | if ( !(code > 47 && code < 58) && // 숫자(0-9) 42 | !(code > 64 && code < 91) && // 대문자(A-Z) 43 | !(code > 96 && code < 123)) { // 소문자(a-z) 44 | return false; 45 | } 46 | return true; 47 | } -------------------------------------------------------------------------------- /src/problem-solving-patterns/definition.md: -------------------------------------------------------------------------------- 1 | # Lecture Node 2 | 3 | https://cs.slides.com/colt_steele/problem-solving-patterns#/ 4 | 5 | # Patterns 6 | 7 | - [Frequency Counter](frequency-counter/definition.md) 8 | - [Multiple Pointers](multiple-pointers/definition.md) 9 | - [Sliding Window](sliding-window/definition.md) 10 | - [Divide and Conquer](divide-and-conquer/definition.md) 11 | - Dynamic Programming 12 | - Greedy Algorithms 13 | - Backtracking 14 | - ... 15 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/divide-and-conquer/count-zeroes.js: -------------------------------------------------------------------------------- 1 | // Divide and Conquer - countZeroes 2 | // Given an array of 1s and 0s which has all 1s first followed by all 0s, 3 | // write a function called countZeroes, which returns the number of zeroes in the array. 4 | 5 | // Time Complexity - O(log n) 6 | 7 | function countZeroes(arr) { 8 | let left = 0; 9 | let right = arr.length - 1; 10 | 11 | while (left <= right) { 12 | const middle = Math.floor((left + right) / 2); 13 | 14 | if (arr[middle] === 1) left = middle + 1; 15 | else right = middle - 1; 16 | } 17 | 18 | return arr.length - left; 19 | } 20 | 21 | console.log(countZeroes([1, 1, 1, 1, 1, 0])); // 1 22 | console.log(countZeroes([1, 1, 1, 1, 0, 0])); // 2 23 | console.log(countZeroes([1, 1, 1, 0, 0, 0])); // 3 24 | console.log(countZeroes([1, 1, 0, 0, 0, 0])); // 4 25 | console.log(countZeroes([1, 0, 0, 0, 0, 0])); // 5 26 | console.log(countZeroes([0, 0, 0])); // 3 27 | console.log(countZeroes([1, 1, 1, 1])); // 0 28 | console.log(countZeroes([1, 0])); // 1 29 | console.log(countZeroes([0])); // 1 30 | console.log(countZeroes([])); // 0 31 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/divide-and-conquer/definition.md: -------------------------------------------------------------------------------- 1 | # Divide and Conquer 2 | 3 | This pattern involves dividing a data set into smaller chunks and then repeating a process with a subset of data. 4 | 5 | This pattern can tremendously decrease time complexity 6 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/divide-and-conquer/find-rotated-index.js: -------------------------------------------------------------------------------- 1 | // Divide and Conquer - findRotatedIndex 2 | // Write a function called findRotatedIndex which accepts a rotated array of sorted 3 | // numbers and an integer. The function should return the index of the integer in the array. 4 | // If the value is not found, return -1. 5 | 6 | // Constraints: 7 | // Time Complexity - O(log n) 8 | // Space Complexity - O(1) 9 | 10 | function findRotatedIndex(arr, num) { 11 | let left = 0; 12 | let right = arr.length - 1; 13 | 14 | if (right && arr[left] >= arr[right]) { 15 | let middle = Math.floor((left + right) / 2); 16 | 17 | while (arr[middle] <= arr[middle + 1]) { 18 | if (arr[left] <= arr[middle]) left = middle + 1; 19 | else right = middle - 1; 20 | 21 | middle = Math.floor((left + right) / 2); 22 | } 23 | 24 | if (num >= arr[0] && num <= arr[middle]) { 25 | left = 0; 26 | right = middle; 27 | } else { 28 | left = middle + 1; 29 | right = arr.length - 1; 30 | } 31 | } 32 | 33 | while (left <= right) { 34 | const middle = Math.floor((left + right) / 2); 35 | 36 | if (num === arr[middle]) return middle; 37 | 38 | if (num > arr[middle]) left = middle + 1; 39 | else right = middle - 1; 40 | } 41 | 42 | return -1; 43 | } 44 | 45 | console.log(findRotatedIndex([3, 4, 1, 2], 4)); // 1 46 | console.log(findRotatedIndex([4, 6, 7, 8, 9, 1, 2, 3, 4], 8)); // 3 47 | console.log(findRotatedIndex([6, 7, 8, 9, 1, 2, 3, 4], 3)); // 6 48 | console.log(findRotatedIndex([37, 44, 66, 102, 10, 22], 14)); // -1 49 | console.log(findRotatedIndex([6, 7, 8, 9, 1, 2, 3, 4], 12)); // -1 50 | console.log(findRotatedIndex([11, 12, 13, 14, 15, 16, 3, 5, 7, 9], 16)); // 5 51 | console.log(findRotatedIndex([11, 12, 13, 17, 39], 17)); // 3 52 | console.log(findRotatedIndex([11], 11)); // 0 53 | console.log(findRotatedIndex([], 11)); // -1 54 | console.log(findRotatedIndex([4, 4, 4, 4, 4], 5)); // -1 55 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/divide-and-conquer/maximum-subarray.js: -------------------------------------------------------------------------------- 1 | // Given an integer array nums, find the contiguous subarray 2 | // (containing at least one number) which has the largest sum and return its sum. 3 | 4 | // Time Complexity O(n * log(n)) 5 | function maxSubArray(nums, start = 0, end = nums.length - 1) { 6 | if (start > end) return -Infinity; 7 | 8 | const middle = Math.floor((start + end) / 2); 9 | 10 | let maxLeft = 0; 11 | let currentSum = 0; 12 | for (let i = middle - 1; i >= start; i--) { 13 | currentSum += nums[i]; 14 | maxLeft = Math.max(maxLeft, currentSum); 15 | } 16 | 17 | let maxRight = 0; 18 | currentSum = 0; 19 | for (let i = middle + 1; i <= end; i++) { 20 | currentSum += nums[i]; 21 | maxRight = Math.max(maxRight, currentSum); 22 | } 23 | 24 | return Math.max(maxLeft + nums[middle] + maxRight, 25 | Math.max(maxSubArray(nums, start, middle - 1), maxSubArray(nums, middle + 1, end))); 26 | }; 27 | 28 | console.log(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])); // 6 29 | console.log(maxSubArray([-1, 0, -2])); // 0 30 | console.log(maxSubArray([-1])); // -1 31 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/divide-and-conquer/sorted-frequency.js: -------------------------------------------------------------------------------- 1 | // Divide and Conquer - sortedFrequency 2 | // Given a sorted array and a number, write a function 3 | // called sortedFrequency that counts the occurrences of the number in the array 4 | 5 | // Time Complexity - O(log n) 6 | 7 | function sortedFrequency(arr, num) { 8 | let left = 0; 9 | let right = arr.length - 1; 10 | 11 | while (left <= right) { 12 | const middle = Math.floor((left + right) / 2); 13 | 14 | if (arr[middle] === num) { 15 | let leftCount = middle; 16 | let rightCount = middle; 17 | 18 | while (arr[leftCount] === num && leftCount >= 0) { 19 | leftCount--; 20 | } 21 | 22 | while (arr[rightCount] === num && rightCount < arr.length) { 23 | rightCount++; 24 | } 25 | 26 | return rightCount - leftCount - 1; 27 | } 28 | 29 | if (arr[middle] < num) left = middle + 1; 30 | else right = middle - 1; 31 | } 32 | 33 | return -1; 34 | } 35 | 36 | console.log(sortedFrequency([1, 1, 2, 2, 2, 2, 3], 2)); // 4 37 | console.log(sortedFrequency([1, 1, 2, 2, 2, 2, 3], 3)); // 1 38 | console.log(sortedFrequency([1, 1, 2, 2, 2, 2, 3], 4)); // -1 39 | console.log(sortedFrequency([], 4)); // -1 40 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/0-pattern.js: -------------------------------------------------------------------------------- 1 | function counter(arr) { 2 | let obj = {} 3 | for(val of arr) { 4 | obj[val] = (obj[val] || 0) + 1; 5 | } 6 | return obj; 7 | } 8 | 9 | console.log(counter([1,2,3,3,5])) -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/are-there-duplicates.js: -------------------------------------------------------------------------------- 1 | // Frequency Counter / Multiple Pointers - areThereDuplicates 2 | // Implement a function called, areThereDuplicates 3 | // which accepts a variable number of arguments, and 4 | // checks whether there are any duplicates among the arguments passed in. 5 | // You can solve this using the frequency counter pattern 6 | // OR the multiple pointers pattern. 7 | 8 | // Examples: 9 | // areThereDuplicates(1, 2, 3) // false 10 | // areThereDuplicates(1, 2, 2) // true 11 | // areThereDuplicates('a', 'b', 'c', 'a') // true 12 | 13 | // Restrictions: 14 | // Time - O(n) 15 | // Space - O(n) 16 | 17 | // Bonus: 18 | // Time - O(n log n) 19 | // Space - O(1) 20 | 21 | function areThereDuplicates(...args) { 22 | if (!args.length) return false; 23 | 24 | const lookup = {}; 25 | 26 | for (const item of args) { 27 | if (lookup[item]) return true; 28 | lookup[item] = 1; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | console.log(areThereDuplicates(1, 2, 3)); // false 35 | console.log(areThereDuplicates('a', 'b', 'c', 'a')); // true 36 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/construct-note.js: -------------------------------------------------------------------------------- 1 | // Frequency Counter - constructNote 2 | // Write a function called constructNote, which accepts two strings, a message and some letters. 3 | // The function should return true if the message can be built with the letters 4 | // that you are given, or it should return false. 5 | // Assume that there are only lowercase letters and no space or 6 | // special characters in both the message and the letters. 7 | 8 | // Bonus Constraints: 9 | // If M is the length of message and N is the length of letters: 10 | // Time Complexity: O(M+N) 11 | // Space Complexity: O(N) 12 | 13 | function constructNote(message, letters) { 14 | const obj = {}; 15 | 16 | for (const char of letters) { 17 | obj[char] = ++obj[char] || 1; 18 | } 19 | 20 | for (const char of message) { 21 | if (!obj[char]) return false; 22 | obj[char]--; 23 | } 24 | 25 | return true; 26 | } 27 | 28 | console.log(constructNote('aa', 'abc')); // false 29 | console.log(constructNote('abc', 'dcba')); // true 30 | console.log(constructNote('aabbcc', 'bcabcaddff')); // true 31 | console.log(constructNote('aabbcc', 'bc')); // false 32 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/definition.md: -------------------------------------------------------------------------------- 1 | # FREQUENCY COUNTERS 2 | 3 | This pattern uses objects or sets to collect values/frequencies of values. 4 | 5 | This can often avoid the need for nested loops or O(N^2) operations with arrays / strings. -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/find-all-duplicates.js: -------------------------------------------------------------------------------- 1 | // Frequency Counter - findAllDuplicates 2 | // Given an array of positive integers, some elements appear twice and others appear once. 3 | // Find all the elements that appear twice in this array. 4 | // Note that you can return the elements in any order. 5 | 6 | // Time Complexity - O(n) 7 | 8 | function findAllDuplicates(arr) { 9 | const obj = {}; 10 | const resultArr = []; 11 | 12 | for (const item of arr) { 13 | obj[item] = ++obj[item] || 1; 14 | if (obj[item] > 1) resultArr.push(item); 15 | } 16 | 17 | return resultArr; 18 | } 19 | 20 | console.log(findAllDuplicates([4, 3, 2, 1, 0])); // [] 21 | console.log(findAllDuplicates([4, 3, 2, 1, 0, 1, 2, 3])); // array with 3, 2 and 1 22 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/find-pair.js: -------------------------------------------------------------------------------- 1 | // Frequency Counter - findPair 2 | // Given an unsorted array and a number n, find if there exists a pair of elements 3 | // in the array whose difference is n. This function should return true 4 | // if the pair exists or false if it does not. 5 | 6 | // Solve this with the following requirements: 7 | // Time Complexity - O(n) 8 | // Space Complexity - O(n) 9 | 10 | function findPair(arr, num) { 11 | const obj = {}; 12 | 13 | for (const item of arr) { 14 | if (obj[item - num] || obj[item + num]) return true; 15 | obj[item] = item; 16 | } 17 | 18 | return false; 19 | } 20 | 21 | console.log(findPair([6, 1, 4, 10, 2, 4], 2)); // true 22 | console.log(findPair([8, 6, 2, 4, 1, 0, 2, 5, 13], 1)); // true 23 | console.log(findPair([4, -2, 3, 10], -6)); // true 24 | console.log(findPair([6, 1, 4, 10, 2, 4], 22)); // false 25 | console.log(findPair([], 0)); // false 26 | console.log(findPair([5, 5], 0)); // true 27 | console.log(findPair([-4, 4], -8)); // true 28 | console.log(findPair([-4, 4], 8)); // true 29 | console.log(findPair([1, 3, 4, 6], -2)); // true 30 | console.log(findPair([0, 1, 3, 4, 6], -2)); // true 31 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/same-array.js: -------------------------------------------------------------------------------- 1 | // Write a function called same, which accepts two arrays. 2 | // The function should return true if every value in the array has 3 | // it's corresponding value squared in the second array. 4 | // The frequency of values must be the same. 5 | 6 | // same([1,2,3], [4,1,9]) // true 7 | // same([1,2,3], [1,9]) // false 8 | // same([1,2,1], [4,4,1]) // false (must be same frequency) 9 | 10 | function same1(arr1, arr2){ 11 | if(arr1.length !== arr2.length){ 12 | return false; 13 | } 14 | for(let i = 0; i < arr1.length; i++){ 15 | let correctIndex = arr2.indexOf(arr1[i] ** 2) 16 | if(correctIndex === -1) { 17 | return false; 18 | } 19 | console.log(arr2); 20 | arr2.splice(correctIndex,1) 21 | } 22 | return true; 23 | } 24 | console.log(same1([1,2,3,2], [9,1,4,4])) 25 | 26 | 27 | function same2(arr1, arr2){ 28 | if(arr1.length !== arr2.length){ 29 | return false; 30 | } 31 | let frequencyCounter1 = {} 32 | let frequencyCounter2 = {} 33 | for(let val of arr1){ 34 | frequencyCounter1[val] = (frequencyCounter1[val] || 0) + 1 35 | } 36 | for(let val of arr2){ 37 | frequencyCounter2[val] = (frequencyCounter2[val] || 0) + 1 38 | } 39 | console.log(frequencyCounter1); 40 | console.log(frequencyCounter2); 41 | for(let key in frequencyCounter1){ 42 | if(!(key ** 2 in frequencyCounter2)){ 43 | return false 44 | } 45 | if(frequencyCounter2[key ** 2] !== frequencyCounter1[key]){ 46 | return false 47 | } 48 | } 49 | return true 50 | } 51 | 52 | console.log(same2([1,2,3,2,5], [9,1,4,4,11])) 53 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/same-frequency.js: -------------------------------------------------------------------------------- 1 | // Frequency Counter - sameFrequency 2 | // Write a function called sameFrequency. 3 | // Given two positive integers, 4 | // find out if the two numbers have the same frequency of digits. 5 | // Your solution MUST have the following complexities: 6 | // Time: O(N) 7 | 8 | // Sample Input: 9 | // sameFrequency(182,281) // true 10 | // sameFrequency(34,14) // fals* 11 | // sameFrequency(3589578, 5879385) // true 12 | // sameFrequency(22,222) // false 13 | 14 | // my solution 15 | function sameFrequency(num1, num2){ 16 | const str1 = num1.toString(); 17 | const str2 = num2.toString(); 18 | 19 | const obj1 = {}; 20 | const obj2 = {}; 21 | 22 | if(str1.length !== str2.length) return false; 23 | 24 | for(let char of str1){ 25 | obj1[char] = (obj1[char] || 0) + 1 26 | } 27 | for(let char of str2){ 28 | obj2[char] = (obj2[char] || 0) + 1 29 | } 30 | for(let key in obj1) { 31 | if (key in obj2) { 32 | if(obj1[key] !== obj2[key]){ 33 | return false; 34 | } 35 | } else { 36 | return false; 37 | } 38 | return true; 39 | } 40 | } 41 | 42 | function sameFrequency(num1, num2) { 43 | const str1 = `${num1}`; 44 | const str2 = `${num2}`; 45 | 46 | if (str1.length !== str2.length) return false; 47 | 48 | const obj = {}; 49 | 50 | for (const char of str1) { 51 | obj[char] = ++obj[char] || 1; 52 | } 53 | 54 | for (const char of str2) { 55 | if (!obj[char]) return false; 56 | obj[char]--; 57 | } 58 | 59 | return true; 60 | } 61 | 62 | console.log(sameFrequency(34, 14)); // false 63 | console.log(sameFrequency(3589578, 5879385)); // true 64 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/frequency-counter/valid_anagram.js: -------------------------------------------------------------------------------- 1 | // Frequency Counter - validAnagram 2 | // Given two strings, write a function to determine if the second string is 3 | // an anagram of the first. An anagram is a word, phrase, or name formed 4 | // by rearranging the letters of another, such as cinema, formed from iceman. 5 | // Note: You may assume the string contains only lowercase alphabets. 6 | 7 | // validAnagram('', '') // true 8 | // validAnagram('aaz', 'zza') // false 9 | // validAnagram('anagram', 'nagaram') // true 10 | // validAnagram("rat","car") // false) // false 11 | // validAnagram('awesome', 'awesom') // false 12 | // validAnagram('qwerty', 'qeywrt') // true 13 | // validAnagram('texttwisttime', 'timetwisttext') // true 14 | 15 | // Time Complexity - O(n) 16 | 17 | // my solution 18 | function validAnagram_bora(str1, str2) { 19 | 20 | if (str1.length !== str2.length) { 21 | return false; 22 | } 23 | 24 | let frequencyCounter1 = {}; 25 | let frequencyCounter2 = {}; 26 | 27 | for (let char of str1) { 28 | frequencyCounter1[char] = (frequencyCounter1[char] || 0) + 1; 29 | } 30 | for (let char of str2) { 31 | frequencyCounter2[char] = (frequencyCounter2[char] || 0) + 1; 32 | } 33 | for (let key in frequencyCounter1) { 34 | if (!(key in frequencyCounter2)) { 35 | return false; 36 | } else if (frequencyCounter1[key] !== frequencyCounter2[key]) { 37 | return false; 38 | } 39 | } 40 | return true; 41 | } 42 | 43 | // colt's solution 44 | function validAnagram_colt(first, second) { 45 | 46 | if (first.length !== second.length) { 47 | return false; 48 | } 49 | 50 | const lookup = {} 51 | 52 | for (let i = 0; i < first.length; i++) { 53 | // if letter exists, incremt, otherwise set to 1 54 | let letter = first[i]; 55 | lookup[letter] ? lookup[letter] += 1 : lookup[letter] = 1; 56 | } 57 | 58 | for (let i = 0; i < second.length; i++) { 59 | let letter = second[i] 60 | // can't find letter or letter is zero then it's not an anagram 61 | if (!lookup[letter]) { 62 | return false; 63 | } else { 64 | lookup[letter] -= 1 65 | } 66 | } 67 | 68 | return true; 69 | } 70 | 71 | // NKaty's solution 72 | 73 | function validAnagram(word1, word2) { 74 | if (word1.length !== word2.length) return false; 75 | 76 | const obj = {}; 77 | 78 | for (const char of word1) { 79 | obj[char] = ++obj[char] || 1; 80 | } 81 | 82 | for (const char of word2) { 83 | if (obj[char]) obj[char]--; 84 | else return false; 85 | } 86 | 87 | return true; 88 | } 89 | 90 | console.log(validAnagram('anagram', 'nagaram')); // true 91 | console.log(validAnagram('rat', 'car')); // false 92 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/is_alpha_numeric.js: -------------------------------------------------------------------------------- 1 | function isAlphaNemeric(char) { 2 | var code = char.charCodeAt(0) 3 | if ( !(code > 47 && code < 58) && // 숫자(0-9) 4 | !(code > 64 && code < 91) && // 대문자(A-Z) 5 | !(code > 96 && code < 123)) { // 소문자(a-z) 6 | return false; 7 | } 8 | return true; 9 | } -------------------------------------------------------------------------------- /src/problem-solving-patterns/multiple-pointers/are-there-duplicates.js: -------------------------------------------------------------------------------- 1 | // Multiple Pointers - areThereDuplicates 2 | // Implement a function called, areThereDuplicates which accepts a variable number of arguments, 3 | // and checks whether there are any duplicates among the arguments passed in. 4 | 5 | // Restrictions: 6 | // Time Complexity - O(n log n) 7 | // Space Complexity - O(1) 8 | 9 | // Solution with two pointers (two variables) 10 | 11 | function areThereDuplicatesWithTwoPointers(...args) { 12 | if (!args.length) return false; 13 | 14 | args.sort(); 15 | 16 | for (let i = 0, j = 1; j < args.length; i++, j++) { 17 | if (args[i] === args[j]) return true; 18 | } 19 | 20 | return false; 21 | } 22 | 23 | console.log(areThereDuplicatesWithTwoPointers(1, 2, 3)); // false 24 | console.log(areThereDuplicatesWithTwoPointers('a', 'b', 'c', 'a')); // true 25 | 26 | // Solution with one pointer (one variable) 27 | 28 | function areThereDuplicatesWithOnePointer(...args) { 29 | if (!args.length) return false; 30 | 31 | args.sort(); 32 | 33 | for (let i = 0; i < args.length - 1; i++) { 34 | if (args[i] === args[i + 1]) return true; 35 | } 36 | 37 | return false; 38 | } 39 | 40 | console.log(areThereDuplicatesWithOnePointer(1, 2, 3)); // false 41 | console.log(areThereDuplicatesWithOnePointer('a', 'b', 'c', 'a')); // true 42 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/multiple-pointers/average-pair.js: -------------------------------------------------------------------------------- 1 | // Multiple Pointers - averagePair 2 | // Write a function called averagePair. Given a sorted array of integers 3 | // and a target average, determine if there is a pair of values in the array 4 | // where the average of the pair equals the target average. 5 | // There may be more than one pair that matches the average target. 6 | 7 | // Bonus Constraints: 8 | // Time Complexity: O(N) 9 | // Space Complexity: O(1) 10 | 11 | function averagePair(arr, av) { 12 | let left = 0; 13 | let right = arr.length - 1; 14 | 15 | while (left < right) { 16 | const tempAv = (arr[left] + arr[right]) / 2; 17 | if (tempAv === av) return true; 18 | if (tempAv > av) right--; 19 | else left++; 20 | } 21 | 22 | return false; 23 | } 24 | 25 | console.log(averagePair([1, 3, 3, 5, 6, 7, 10, 12, 19], 8)); // true 26 | console.log(averagePair([-1, 0, 3, 4, 5, 6], 4.1)); // false 27 | console.log(averagePair([], 4)); // false 28 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/multiple-pointers/count-unique-values.js: -------------------------------------------------------------------------------- 1 | // Multiple Pointers - countUniqueValues 2 | // Implement a function called countUniqueValues, which accepts a sorted array, 3 | // and counts the unique values in the array. 4 | // There can be negative numbers in the array, but it will always be sorted. 5 | 6 | // countUniqueValues([1,1,1,1,1,2]) // 2 7 | // countUniqueValues([1,2,3,4,4,4,7,7,12,12,13]) // 7 8 | // countUniqueValues([]) // 0 9 | // countUniqueValues([-2,-1,-1,0,1]) // 4 10 | 11 | // Time Complexity - O(n) 12 | // Space Complexity - O(n) 13 | 14 | // Bonus 15 | // You must do this with constant or O(1) space and O(n) time. 16 | 17 | 18 | // my solution && colt's solution 19 | function countUniqueValues(arr) { 20 | if(arr.length === 0) return 0; 21 | let i = 0; 22 | for (let j = 1; j < arr.length; j++) { 23 | if (arr[i] !== arr[j]) { 24 | i++; 25 | arr[i] = arr[j]; 26 | } 27 | console.log(arr) 28 | } 29 | return i + 1; 30 | } 31 | 32 | console.log(countUniqueValues([1, 2, 3, 4, 4, 4, 7, 7, 12, 12, 13])); // 7 33 | console.log(countUniqueValues([-2, -1, -1, 0, 1])); // 4 34 | 35 | // Solution with two pointers (two variables) 36 | 37 | function countUniqueValuesWithTwoPointers(arr) { 38 | if (!arr.length) return 0; 39 | let counter = 1; 40 | 41 | for (let i = 0, j = 1; j < arr.length; j++, i++) { 42 | if (arr[i] !== arr[j]) counter++; 43 | } 44 | 45 | return counter; 46 | } 47 | 48 | console.log(countUniqueValuesWithTwoPointers([1, 2, 3, 4, 4, 4, 7, 7, 12, 12, 13])); // 7 49 | console.log(countUniqueValuesWithTwoPointers([-2, -1, -1, 0, 1])); // 4 50 | 51 | // Solution with one pointer (one variable) 52 | 53 | function countUniqueValuesWithOnePointer(arr) { 54 | if (!arr.length) return 0; 55 | let counter = 1; 56 | 57 | for (let i = 0; i < arr.length - 1; i++) { 58 | if (arr[i] !== arr[i + 1]) counter++; 59 | } 60 | 61 | return counter; 62 | } 63 | 64 | console.log(countUniqueValuesWithOnePointer([1, 2, 3, 4, 4, 4, 7, 7, 12, 12, 13])); // 7 65 | console.log(countUniqueValuesWithOnePointer([-2, -1, -1, 0, 1])); // 4 66 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/multiple-pointers/definition.md: -------------------------------------------------------------------------------- 1 | # MULTIPLE POINTERS 2 | 3 | Creating pointers or values that correspond to an index or position and move towards the beginning, end or middle based on a certain condition. 4 | 5 | Very efficient for solving problems with minimal space complexity as well. -------------------------------------------------------------------------------- /src/problem-solving-patterns/multiple-pointers/find-pair.js: -------------------------------------------------------------------------------- 1 | // Frequency Counter - findPair 2 | // Given an unsorted array and a number n, find if there exists a pair of elements 3 | // in the array whose difference is n. This function should return true 4 | // if the pair exists or false if it does not. 5 | 6 | // Solve this with the following requirements: 7 | // Time Complexity - O(n log n) 8 | // Space Complexity - O(1) 9 | 10 | function findPair(arr, num) { 11 | arr.sort((a, b) => a - b); 12 | 13 | const numAbs = Math.abs(num); 14 | let i = 0; 15 | let j = 1; 16 | 17 | while (j < arr.length) { 18 | const diffAbs = Math.abs(arr[i] - arr[j]); 19 | 20 | if (diffAbs === numAbs) return true; 21 | 22 | if (diffAbs > numAbs && i === j - 1) { 23 | i++; 24 | j++; 25 | } else if (diffAbs > numAbs) { 26 | i++; 27 | } else { 28 | j++; 29 | } 30 | } 31 | 32 | return false; 33 | } 34 | 35 | console.log(findPair([6, 1, 4, 10, 2, 4], 2)); // true 36 | console.log(findPair([8, 6, 2, 4, 1, 0, 2, 5, 13], 1)); // true 37 | console.log(findPair([4, -2, 3, 10], -6)); // true 38 | console.log(findPair([6, 1, 4, 10, 2, 4], 22)); // false 39 | console.log(findPair([], 0)); // false 40 | console.log(findPair([5, 5], 0)); // true 41 | console.log(findPair([-4, 4], -8)); // true 42 | console.log(findPair([-4, 4], 8)); // true 43 | console.log(findPair([1, 3, 4, 6], -2)); // true 44 | console.log(findPair([0, 1, 3, 4, 6], -2)); // true 45 | console.log(findPair([-2, 5, 10, 15, 16], 0)); // false 46 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/multiple-pointers/is-subsequence.js: -------------------------------------------------------------------------------- 1 | // Multiple Pointers - isSubsequence 2 | // Write a function called isSubsequence which takes in two strings and checks 3 | // whether the characters in the first string form a subsequence of the characters 4 | // in the second string. In other words, the function should check whether the characters 5 | // in the first string appear somewhere in the second string, without their order changing. 6 | 7 | // Your solution MUST have AT LEAST the following complexities: 8 | // Time Complexity - O(N + M) 9 | // Space Complexity - O(1) 10 | 11 | function isSubsequence(pattern, str) { 12 | if (str.length < pattern.length) return false; 13 | 14 | let i = 0; 15 | let j = 0; 16 | 17 | while (j < str.length && i < pattern.length) { 18 | if (pattern[i] === str[j]) i++; 19 | j++; 20 | } 21 | 22 | return i === pattern.length; 23 | } 24 | 25 | console.log(isSubsequence('sing', 'sting')); // true 26 | console.log(isSubsequence('abc', 'abracadabra')); // true 27 | console.log(isSubsequence('abc', 'acb')); // false (order matters) 28 | console.log(isSubsequence('', 'cat')); // true 29 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/multiple-pointers/sum-zero.js: -------------------------------------------------------------------------------- 1 | // Write a function called sumZero which accepts a sorted array of integers. 2 | // The function should find the first pair where the sum is 0. 3 | // Return an array that includes both values that sum to zero or undefined 4 | // if a pair does not exist 5 | 6 | // sumZero([-3,-2,-1,0,1,2,3]) // [-3,3] 7 | // sumZero([-2,0,1,3]) // undefined 8 | // sumZero([1,2,3]) // undefined 9 | 10 | function sumZero(arr){ 11 | let left = 0; 12 | let right = arr.length - 1; 13 | while(left < right){ 14 | let sum = arr[left] + arr[right]; 15 | if(sum === 0){ 16 | return [arr[left], arr[right]]; 17 | } else if(sum > 0){ 18 | right--; 19 | } else { 20 | left++; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/problem-solving-patterns/sliding-window/definition.md: -------------------------------------------------------------------------------- 1 | # SLIDING WINDOW 2 | 3 | This pattern involves creating a window which can either be an array or number from one position to another 4 | 5 | Depending on a certain condition, the window either increases or closes (and a new window is created) 6 | 7 | Very useful for keeping track of a subset of data in an array/string etc. -------------------------------------------------------------------------------- /src/problem-solving-patterns/sliding-window/find-longest-substring.js: -------------------------------------------------------------------------------- 1 | // Sliding Window - findLongestSubstring 2 | // Write a function called findLongestSubstring, which accepts a string and 3 | // returns the length of the longest substring with all distinct characters. 4 | 5 | // Time Complexity - O(n^2) 6 | 7 | function findLongestSubstringVersion1(str) { 8 | let obj = {}; 9 | let i = 0; 10 | let maxLen = 0; 11 | let tempLen = 0; 12 | while (i < str.length) { 13 | if (typeof obj[str[i]] !== 'undefined') { 14 | tempLen = 0; 15 | i = obj[str[i]] + 1; 16 | obj = {}; 17 | } else { 18 | obj[str[i]] = i; 19 | tempLen++; 20 | i++; 21 | } 22 | maxLen = Math.max(maxLen, tempLen); 23 | } 24 | 25 | return maxLen; 26 | } 27 | 28 | console.log(findLongestSubstringVersion1('')); // 0 29 | console.log(findLongestSubstringVersion1('rithmschool')); // 7 30 | console.log(findLongestSubstringVersion1('thisisawesome')); // 6 31 | 32 | // Time Complexity - O(n) 33 | 34 | function findLongestSubstringVersion2(str) { 35 | const obj = {}; 36 | let maxLen = 0; 37 | let start = 0; 38 | 39 | for (let i = 0; i < str.length; i++) { 40 | if (obj[str[i]]) { 41 | start = Math.max(start, obj[str[i]]); 42 | } 43 | 44 | obj[str[i]] = i + 1; 45 | maxLen = Math.max(maxLen, i - start + 1); 46 | } 47 | 48 | return maxLen; 49 | } 50 | 51 | console.log(findLongestSubstringVersion2('')); // 0 52 | console.log(findLongestSubstringVersion2('rithmschool')); // 7 53 | console.log(findLongestSubstringVersion2('thisisawesome')); // 6 54 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/sliding-window/max-subarray-sum.js: -------------------------------------------------------------------------------- 1 | // Sliding Window - maxSubarraySum 2 | // Given an array of integers and a number, write a function called maxSubarraySum, 3 | // which finds the maximum sum of a subarray with the length of the number passed to the function. 4 | 5 | // Note that a subarray must consist of consecutive elements from the original array. 6 | // In the first example below, [100, 200, 300] is a subarray of the original array, but [100, 300] is not. 7 | // maxSubarraySum([100,200,300,400], 2) // 700 8 | 9 | // maxSubarraySum([1,2,5,2,8,1,5],2) // 10 10 | // maxSubarraySum([1,2,5,2,8,1,5],4) // 17 11 | // maxSubarraySum([4,2,1,6],1) // 6 12 | // maxSubarraySum([4,2,1,6,2],4) // 13 13 | // maxSubarraySum([],4) // null 14 | 15 | // Constraints: 16 | // Time Complexity - O(N) 17 | // Space Complexity - O(1) 18 | 19 | // A naive solution 20 | // Time Complexity - O(N^2) 21 | function maxSubarraySum(arr, num) { 22 | if ( num > arr.length){ 23 | return null; 24 | } 25 | var max = -Infinity; 26 | for (let i = 0; i < arr.length - num + 1; i ++){ 27 | temp = 0; 28 | for (let j = 0; j < num; j++){ 29 | temp += arr[i + j]; 30 | } 31 | if (temp > max) { 32 | max = temp; 33 | } 34 | } 35 | return max; 36 | } 37 | 38 | // Refactor 39 | // Time Complexity - O(N) 40 | // colt's solution 41 | function maxSubarraySum(arr, num){ 42 | if (arr.length < num) return null; 43 | 44 | let maxSum = 0; 45 | let tempSum = 0; 46 | 47 | for (let i = 0; i < num; i++) { 48 | maxSum += arr[i]; 49 | } 50 | tempSum = maxSum; 51 | for (let i = num; i < arr.length; i++) { 52 | tempSum = tempSum - arr[i - num] + arr[i]; 53 | maxSum = Math.max(maxSum, tempSum); 54 | } 55 | return maxSum; 56 | } 57 | 58 | // NKaty's solution 59 | function maxSubarraySum(arr, num) { 60 | if (arr.length < num) return null; 61 | 62 | let tempSum = 0; 63 | 64 | for (let i = 0; i < num; i++) { 65 | tempSum += arr[i]; 66 | } 67 | 68 | let maxSum = tempSum; 69 | 70 | for (let i = 0; i < arr.length - num; i++) { 71 | tempSum = tempSum - arr[i] + arr[i + num]; 72 | maxSum = Math.max(maxSum, tempSum); 73 | } 74 | 75 | return maxSum; 76 | } 77 | 78 | console.log(maxSubarraySum([1, 4, 2, 10, 23, 3, 1, 0, 20], 4)); // 39 79 | console.log(maxSubarraySum([3, -2, 7, -4, 1, -1, 4, -2, 1], 2)); // 5 80 | console.log(maxSubarraySum([2, 3], 3)); // null 81 | -------------------------------------------------------------------------------- /src/problem-solving-patterns/sliding-window/min-suarray-length.js: -------------------------------------------------------------------------------- 1 | // Sliding Window - minSubArrayLen 2 | // Write a function called minSubArrayLen which accepts two parameters - 3 | // an array of positive integers and a positive integer. 4 | 5 | // This function should return the minimal length of a contiguous subarray 6 | // of which the sum is greater than or equal to the integer passed to the function. 7 | // If there isn't one, return 0 instead. 8 | 9 | // Time Complexity - O(n) 10 | // Space Complexity - O(1) 11 | 12 | function minSubArrayLen(arr, num) { 13 | let tempLen = 0; 14 | let sum = 0; 15 | 16 | while (sum < num && tempLen < arr.length) { 17 | sum += arr[tempLen]; 18 | tempLen++; 19 | } 20 | 21 | if (sum < num) return 0; 22 | 23 | let minLen = tempLen; 24 | let i = 0; 25 | 26 | while (i + tempLen - 1 < arr.length) { 27 | if (sum >= num) { 28 | minLen = Math.min(minLen, tempLen); 29 | sum -= arr[i]; 30 | i++; 31 | tempLen--; 32 | } else { 33 | sum += arr[i + tempLen]; 34 | tempLen++; 35 | } 36 | } 37 | 38 | return minLen; 39 | } 40 | 41 | console.log(minSubArrayLen([2, 3, 1, 2, 4, 3], 7)); // 2 -> because [4,3] is the smallest subarray 42 | console.log(minSubArrayLen([2, 1, 6, 5, 4], 9)); // 2 -> because [5,4] is the smallest subarray 43 | console.log(minSubArrayLen([3, 1, 7, 11, 2, 9, 8, 21, 62, 33, 19], 52)); // 1 -> because [62] is greater than 52 44 | -------------------------------------------------------------------------------- /src/recursion/capitalize-first.js: -------------------------------------------------------------------------------- 1 | // capitalizeFirst 2 | // Write a recursive function called capitalizeFirst. 3 | // Given an array of strings, capitalize the first letter of each string in the array. 4 | 5 | function capitalizeFirst(arr) { 6 | const result = []; 7 | 8 | if (!arr.length) return result; 9 | 10 | result.push(arr[0][0].toUpperCase() + arr[0].slice(1)); 11 | 12 | return result.concat(capitalizeFirst(arr.slice(1))); 13 | } 14 | 15 | console.log(capitalizeFirst(['car', 'taco', 'banana'])); // ['Car','Taco','Banana'] 16 | -------------------------------------------------------------------------------- /src/recursion/capitalize-words.js: -------------------------------------------------------------------------------- 1 | // capitalizeWords 2 | // Write a recursive function called capitalizeWords. 3 | // Given an array of words, return a new array containing each word capitalized. 4 | 5 | function capitalizeWords(arr) { 6 | const result = []; 7 | 8 | if (!arr.length) return result; 9 | 10 | result.push(arr[0].toUpperCase()); 11 | 12 | return [...result, ...capitalizeWords(arr.slice(1))]; 13 | } 14 | 15 | const words = ['i', 'am', 'learning', 'recursion']; 16 | console.log(capitalizeWords(words)); // ['I', 'AM', 'LEARNING', 'RECURSION'] 17 | -------------------------------------------------------------------------------- /src/recursion/collect-strings.js: -------------------------------------------------------------------------------- 1 | // collectStrings 2 | // Write a function called collectStrings which accepts an object and 3 | // returns an array of all the values in the object that have a typeof string. 4 | 5 | function collectStrings (obj) { 6 | let resultArr = []; 7 | 8 | for (const key in obj) { 9 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 10 | if (typeof obj[key] === 'string') resultArr.push(obj[key]); 11 | if (typeof obj[key] === 'object') resultArr = resultArr.concat(collectStrings(obj[key])); 12 | } 13 | } 14 | 15 | return resultArr; 16 | } 17 | 18 | const obj = { 19 | stuff: 'foo', 20 | data: { 21 | val: { 22 | thing: { 23 | info: 'bar', 24 | moreInfo: { 25 | evenMoreInfo: { 26 | weMadeIt: 'baz' 27 | } 28 | } 29 | } 30 | } 31 | } 32 | }; 33 | 34 | console.log(collectStrings(obj)); // ['foo', 'bar', 'baz']) 35 | -------------------------------------------------------------------------------- /src/recursion/definition.md: -------------------------------------------------------------------------------- 1 | ## What is recursion? 2 | 3 | A process (a function in our case) that calls itself. 4 | 5 | ## Lecture Node 6 | 7 | https://cs.slides.com/colt_steele/searching-algorithms-22#/ -------------------------------------------------------------------------------- /src/recursion/factorial.js: -------------------------------------------------------------------------------- 1 | // factorial 2 | // Write a function factorial which accepts a number and returns the factorial of that number. 3 | // A factorial is the product of an integer and all the integers below it; 4 | // e.g., factorial four ( 4! ) is equal to 24, because 4 * 3 * 2 * 1 equals 24. 5 | // factorial zero (0!) is always 1. 6 | 7 | // my solution 8 | function factorial(num){ 9 | if(num === 0){ // num <=1 로 바꾸는게 좋을것같다! 10 | return 1; 11 | } 12 | 13 | return num * factorial(num - 1); 14 | 15 | } 16 | 17 | function factorial(num) { 18 | if (num <= 1) return 1; 19 | 20 | return num * factorial(num - 1); 21 | } 22 | 23 | console.log(factorial(1)); // 1 24 | console.log(factorial(2)); // 2 25 | console.log(factorial(4)); // 24 26 | console.log(factorial(7)); // 5040 27 | -------------------------------------------------------------------------------- /src/recursion/fibonacci.js: -------------------------------------------------------------------------------- 1 | // fib 2 | // Write a recursive function called fib which accepts a number 3 | // and returns the nth number in the Fibonacci sequence. 4 | // Recall that the Fibonacci sequence is the sequence of whole numbers 1, 1, 2, 3, 5, 8, ... 5 | // which starts with 1 and 1, and where every number thereafter 6 | // is equal to the sum of the previous two numbers. 7 | 8 | // my solution 1 9 | function fib(num){ 10 | // add whatever parameters you deem necessary - good luck! 11 | if (num === 1 || num === 2) { 12 | return 1; 13 | } 14 | 15 | return fib(num - 1) + fib(num - 2); 16 | } 17 | 18 | // my solution 2 19 | function fib(num) { 20 | return num <= 1 ? num : fib(num - 1) + fib(num - 2); 21 | } 22 | 23 | // colt's solution 24 | function fib(num) { 25 | if (num < 2) return num; 26 | 27 | return fib(num - 1) + fib(num - 2); 28 | } 29 | 30 | console.log(fib(4));// 3 31 | console.log(fib(10)); // 55 32 | console.log(fib(28)); // 317811 33 | console.log(fib(35)); // 9227465 34 | -------------------------------------------------------------------------------- /src/recursion/flatten.js: -------------------------------------------------------------------------------- 1 | // flatten 2 | // Write a recursive function called flatten which accepts an array of arrays 3 | // and returns a new array with all values flattened. 4 | 5 | function flatten(arr) { 6 | let resultArr = []; 7 | 8 | for (const item of arr) { 9 | if (item instanceof Array) resultArr = resultArr.concat(flatten(item)); 10 | else resultArr.push(item); 11 | } 12 | 13 | return resultArr; 14 | } 15 | 16 | console.log(flatten([1, 2, 3, [4, 5]])); // [1, 2, 3, 4, 5] 17 | console.log(flatten([1, [2, [3, 4], [[5]]]])); // [1, 2, 3, 4, 5] 18 | console.log(flatten([[1], [2], [3]])); // [1, 2, 3] 19 | console.log(flatten([[[[1], [[[2]]], [[[[[[[3]]]]]]]]]])); // [1, 2, 3] 20 | -------------------------------------------------------------------------------- /src/recursion/helper_method_recursion/collect_odd_values.js: -------------------------------------------------------------------------------- 1 | // Let's try to collect all of the odd values in an array! 2 | 3 | function collectOddValues(arr){ 4 | 5 | let result = [] 6 | 7 | function helper(helperInput){ 8 | if(helperInput.length === 0) { 9 | return; 10 | } 11 | 12 | if(helperInput[0] % 2 !== 0){ 13 | result.push(helperInput[0]) 14 | } 15 | 16 | helper(helperInput.slice(1)) 17 | } 18 | 19 | helper(arr) 20 | 21 | return result; 22 | 23 | } 24 | 25 | // You could reference the helper method pattern on 26 | // src\recursion\recursion_helper_method\pattern.js -------------------------------------------------------------------------------- /src/recursion/helper_method_recursion/pattern.js: -------------------------------------------------------------------------------- 1 | function outer(input){ 2 | 3 | var outerScopedVariable = [] 4 | 5 | function helper(helperInput){ 6 | // modify the outerScopedVariable 7 | helper(helperInput--) 8 | } 9 | 10 | helper(input) 11 | 12 | return outerScopedVariable; 13 | 14 | } -------------------------------------------------------------------------------- /src/recursion/is-palindrome.js: -------------------------------------------------------------------------------- 1 | // isPalindrome 2 | // Write a recursive function called isPalindrome which returns true 3 | // if the string passed to it is a palindrome (reads the same forward and backward). 4 | // Otherwise it returns false. 5 | 6 | function isPalindrome(str) { 7 | if (!str.length) return true; 8 | 9 | if (str[0] !== str[str.length - 1]) return false; 10 | 11 | return isPalindrome(str.slice(1, -1)); 12 | } 13 | 14 | console.log(isPalindrome('amanaplanacanalpanama')); // true 15 | console.log(isPalindrome('amanaplanacanalpandemonium')); // false 16 | -------------------------------------------------------------------------------- /src/recursion/nested-even-sum.js: -------------------------------------------------------------------------------- 1 | // nestedEvenSum 2 | // Write a recursive function called nestedEvenSum. 3 | // Return the sum of all even numbers in an object which may contain nested objects. 4 | 5 | function nestedEvenSum(obj) { 6 | let sum = 0; 7 | 8 | for (const key in obj) { 9 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 10 | if (typeof obj[key] === 'number' && obj[key] % 2 === 0) sum += obj[key]; 11 | if (typeof obj[key] === 'object') sum += nestedEvenSum(obj[key]); 12 | } 13 | } 14 | 15 | return sum; 16 | } 17 | 18 | const obj1 = { 19 | outer: 2, 20 | obj: { 21 | inner: 2, 22 | otherObj: { 23 | superInner: 2, 24 | notANumber: true, 25 | alsoNotANumber: 'yup' 26 | } 27 | } 28 | }; 29 | 30 | const obj2 = { 31 | a: 2, 32 | b: { b: 2, bb: { b: 3, bb: { b: 2 } } }, 33 | c: { c: { c: 2 }, cc: 'ball', ccc: 5 }, 34 | d: 1, 35 | e: { e: { e: 2 }, ee: 'car' } 36 | }; 37 | 38 | console.log(nestedEvenSum(obj1)); // 6 39 | console.log(nestedEvenSum(obj2)); // 10 40 | -------------------------------------------------------------------------------- /src/recursion/power.js: -------------------------------------------------------------------------------- 1 | // power 2 | // Write a function called power which accepts a base and an exponent. 3 | // The function should return the power of the base to the exponent. 4 | // This function should mimic the functionality of Math.pow(). 5 | // Do not worry about negative bases and exponents. 6 | 7 | function power(base, exponent){ 8 | if (base === 1 || exponent === 0) { 9 | return 1; 10 | } 11 | 12 | return base * power(base, exponent - 1); 13 | 14 | } 15 | 16 | 17 | function power(num, pow) { 18 | if (pow === 0) return 1; 19 | 20 | return num * power(num, pow - 1); 21 | } 22 | 23 | console.log(power(2, 0)); // 1 24 | console.log(power(2, 2)); // 4 25 | console.log(power(2, 4)); // 16 26 | -------------------------------------------------------------------------------- /src/recursion/product-of-array.js: -------------------------------------------------------------------------------- 1 | // productOfArray 2 | // Write a function called productOfArray which takes in an array of numbers 3 | // and returns the product of them all. 4 | 5 | function productOfArray(arr) { 6 | 7 | if(arr.length === 0){ 8 | return 1; 9 | } 10 | 11 | return arr[0] * productOfArray(arr.slice(1)); 12 | 13 | } 14 | 15 | 16 | function productOfArray(arr) { 17 | if (!arr.length) return 1; 18 | 19 | return arr[0] * productOfArray(arr.slice(1)); 20 | } 21 | console.log(productOfArray([1, 2, 3])); // 6 22 | console.log(productOfArray([1, 2, 3, 10])); // 60 23 | -------------------------------------------------------------------------------- /src/recursion/recursive-range.js: -------------------------------------------------------------------------------- 1 | // recursiveRange 2 | // Write a function called recursiveRange which accepts a number 3 | // and adds up all the numbers from 0 to the number passed to the function. 4 | 5 | // my solution 6 | function recursiveRange(num){ 7 | if(num === 0) { // num이 음수인경우 처리 해줘야함 8 | return 0; 9 | } 10 | 11 | let sum = 0; 12 | 13 | return num + recursiveRange(num - 1); 14 | 15 | } 16 | 17 | 18 | 19 | function recursiveRange(num) { 20 | if (num <= 0) return 0; 21 | 22 | return num + recursiveRange(num - 1); 23 | } 24 | 25 | console.log(recursiveRange(6)); // 21 26 | console.log(recursiveRange(10)); // 55 27 | -------------------------------------------------------------------------------- /src/recursion/reverse.js: -------------------------------------------------------------------------------- 1 | // reverse 2 | // Write a recursive function called reverse 3 | // which accepts a string and returns a new string in reverse. 4 | 5 | function reverse(str) { 6 | if (str.length <= 1) return str; 7 | 8 | return str[str.length - 1] + reverse(str.slice(0, str.length - 1)); 9 | } 10 | 11 | console.log(reverse('awesome')); // 'emosewa' 12 | console.log(reverse('rithmschool')); // 'loohcsmhtir' 13 | -------------------------------------------------------------------------------- /src/recursion/some-recursive.js: -------------------------------------------------------------------------------- 1 | // someRecursive 2 | // Write a recursive function called someRecursive which accepts an array and a callback. 3 | // The function returns true if a single value in the array returns true 4 | // when passed to the callback. Otherwise it returns false. 5 | 6 | function someRecursive(arr, cb) { 7 | if (!arr.length) return false; 8 | 9 | if (!cb(arr[0])) return someRecursive(arr.slice(1), cb); 10 | 11 | return true; 12 | } 13 | 14 | console.log(someRecursive([1, 2, 3, 4], val => val % 2 !== 0)); // true 15 | console.log(someRecursive([4, 6, 8, 9], val => val % 2 !== 0)); // true 16 | console.log(someRecursive([4, 6, 8], val => val % 2 !== 0)); // false 17 | console.log(someRecursive([4, 6, 8], val => val > 10)); // false 18 | -------------------------------------------------------------------------------- /src/recursion/stringify-numbers.js: -------------------------------------------------------------------------------- 1 | // stringifyNumbers 2 | // Write a function called stringifyNumbers which takes in an object and 3 | // finds all of the values which are numbers and converts them to strings. 4 | // Recursion would be a great way to solve this! 5 | 6 | function stringifyNumbers(obj) { 7 | const newObj = Object.assign({}, obj); 8 | 9 | for (const key in newObj) { 10 | if (Object.prototype.hasOwnProperty.call(newObj, key)) { 11 | if (typeof newObj[key] === 'number') newObj[key] = newObj[key].toString(); 12 | if (typeof newObj[key] === 'object') newObj[key] = stringifyNumbers(newObj[key]); 13 | } 14 | } 15 | 16 | return newObj; 17 | } 18 | 19 | const obj = { 20 | num: 1, 21 | test: [], 22 | data: { 23 | val: 4, 24 | info: { 25 | isRight: true, 26 | random: 66 27 | } 28 | } 29 | }; 30 | 31 | console.log(stringifyNumbers(obj)); 32 | // { num: '1', 33 | // test: {}, 34 | // data: { val: '4', info: { isRight: true, random: '66' } } } 35 | -------------------------------------------------------------------------------- /src/rosetta-code/100-doors.js: -------------------------------------------------------------------------------- 1 | // There are 100 doors in a row that are all initially closed. 2 | // You make 100 passes by the doors. The first time through, 3 | // visit every door and 'toggle' the door (if the door is closed, open it; 4 | // if it is open, close it). The second time, only visit every 2nd door 5 | // (i.e., door #2, #4, #6, ...) and toggle it. The third time, visit every 3rd door 6 | // (i.e., door #3, #6, #9, ...), etc., until you only visit the 100th door. 7 | 8 | // Implement a function to determine the state of the doors after the last pass. 9 | // Return the final result in an array, with only the door number 10 | // included in the array if it is open. 11 | 12 | function getFinalOpenedDoors (numDoors) { 13 | const doors = Array.from({ length: 100 }, () => false); 14 | 15 | for (let j = 1; j <= 100; j++) { 16 | for (let i = j - 1; i < doors.length; i++) { 17 | if ((i + 1) % j === 0) doors[i] = !doors[i]; 18 | } 19 | } 20 | 21 | return doors.reduce((acc, item, index) => { 22 | if (item === true) acc.push(index + 1); 23 | return acc; 24 | }, []); 25 | } 26 | 27 | console.log(getFinalOpenedDoors()); // [ 1, 4, 9, 16, 25, 36, 49, 64, 81, 100 ] 28 | -------------------------------------------------------------------------------- /src/rosetta-code/9-billion-names-of-god.js: -------------------------------------------------------------------------------- 1 | // This task is a variation of the short story by Arthur C. Clarke. 2 | // In detail, to specify what is meant by a “name”: 3 | // The integer 1 has 1 name “1”. 4 | // The integer 2 has 2 names “1+1”, and “2”. 5 | // The integer 3 has 3 names “1+1+1”, “2+1”, and “3”. 6 | // The integer 4 has 5 names “1+1+1+1”, “2+1+1”, “2+2”, “3+1”, “4”. 7 | 8 | function numberOfNamesTD (num) { 9 | const values = Array.from({ length: num }, (e, i) => i + 1); 10 | const index = values.length - 1; 11 | const memo = {}; 12 | 13 | function countVariations(num, index) { 14 | if (num < 0 || index < 0) return 0; 15 | if (num === 0) return 1; 16 | 17 | const key = `${index}/${num}`; 18 | 19 | if (memo[key]) return memo[key]; 20 | 21 | memo[key] = countVariations(num - values[index], index) + countVariations(num, index - 1); 22 | 23 | return memo[key]; 24 | } 25 | return countVariations(num, index); 26 | } 27 | 28 | console.log(numberOfNamesTD(123)); // 2552338241 29 | 30 | function numberOfNamesBU(num) { 31 | const values = Array.from({ length: num }, (e, i) => i + 1); 32 | const table = Array.from({ length: num + 1 }).fill(0); 33 | table[0] = 1; 34 | 35 | for (let i = 0; i < values.length; i++) { 36 | for (let j = values[i]; j <= num; j++) { 37 | table[j] += table[j - values[i]]; 38 | } 39 | } 40 | 41 | return table[num]; 42 | } 43 | 44 | console.log(numberOfNamesBU(12345)); // 6.942035795392596e+118 45 | -------------------------------------------------------------------------------- /src/rosetta-code/abc-problem.js: -------------------------------------------------------------------------------- 1 | // You are given a collection of ABC blocks (e.g., childhood alphabet blocks). 2 | // There are 20 blocks with two letters on each block. A complete alphabet is 3 | // guaranteed amongst all sides of the blocks. 4 | // Write a function that takes a string (word) and determines whether 5 | // the word can be spelled with the given collection of blocks. 6 | // Once a letter on a block is used that block cannot be used again. 7 | // The function should be case-insensitive. 8 | 9 | function canMakeWord ( 10 | word, 11 | characters = 'BO XK DQ CP NA GT RE TG QD FS JW HU VI AN OB ER FS LY PC ZM' 12 | ) { 13 | const wordArray = word.toUpperCase().split(''); 14 | const charArray = characters.split(' ').map(item => item.split('')); 15 | 16 | const charObject = charArray.reduce((acc, item) => { 17 | item.forEach((char) => { 18 | acc[char] = ++acc[char] || 1; 19 | }); 20 | 21 | return acc; 22 | }, {}); 23 | 24 | const check = (arr, obj) => { 25 | if (arr.length === 0) { 26 | return wordArray.every(char => --obj[char] > -1); 27 | } 28 | 29 | const item = arr[0]; 30 | const newArr = arr.slice(1); 31 | 32 | if (wordArray.includes(item[0]) && wordArray.includes(item[1])) { 33 | const newObj0 = { ...obj }; 34 | const newObj1 = { ...obj }; 35 | newObj0[item[0]]--; 36 | newObj1[item[1]]--; 37 | return check(newArr, newObj0) || check(newArr, newObj1); 38 | } else return check(newArr, { ...obj }); 39 | }; 40 | 41 | return check(charArray, charObject); 42 | } 43 | 44 | console.log(canMakeWord('')); // true 45 | console.log(canMakeWord('A')); // true 46 | console.log(canMakeWord('bark')); // true 47 | console.log(canMakeWord('BooK')); // false 48 | console.log(canMakeWord('BooK', 'BO OK DQ OJ CP NA GT RE TG QD FS JW BU VI AN ER FS LY PC ZM')); // true 49 | console.log(canMakeWord('TReAT')); // true 50 | console.log(canMakeWord('COMMON')); // false 51 | console.log(canMakeWord('squAD')); // true 52 | console.log(canMakeWord('conFUSE')); // true 53 | -------------------------------------------------------------------------------- /src/rosetta-code/abundant-deficient-and-perfect-number-classifications.js: -------------------------------------------------------------------------------- 1 | // These define three classifications of positive integers based on their proper divisors. 2 | // Let P(n) be the sum of the proper divisors of n where proper divisors are 3 | // all positive integers n other than n itself. 4 | // If P(n) < n then n is classed as "deficient" 5 | // If P(n) === n then n is classed as "perfect" 6 | // If P(n) > n then n is classed as "abundant" 7 | // Implement a function that calculates how many of the integers 8 | // from 1 to 20,000 (inclusive) are in each of the three classes. 9 | // Output the result as an array in the following format [deficient, perfect, abundant]. 10 | 11 | function getDPA (num) { 12 | let deficient = 1; 13 | let perfect = 0; 14 | let abundant = 0; 15 | let sum; 16 | 17 | for (let i = 2; i <= num; i++) { 18 | sum = 0; 19 | 20 | for (let j = 1; j <= i / 2; j++) { 21 | if (i % j === 0) sum += j; 22 | if (sum > i) break; 23 | } 24 | 25 | if (sum < i) deficient++; 26 | else if (sum > i) abundant++; 27 | else perfect++; 28 | } 29 | 30 | return [deficient, perfect, abundant]; 31 | } 32 | 33 | console.log(getDPA(20000)); // [ 15043, 4, 4953 ] 34 | -------------------------------------------------------------------------------- /src/rosetta-code/accumulator-factory.js: -------------------------------------------------------------------------------- 1 | // Create a function that takes a single (numeric) argument and 2 | // returns another function that is an accumulator. 3 | // The returned accumulator function in turn also takes a single numeric argument, 4 | // and returns the sum of all the numeric values passed in so far to that accumulator 5 | // (including the initial value passed when the accumulator was created). 6 | 7 | const accumulator = sum => num => { 8 | sum += num; 9 | return sum; 10 | }; 11 | 12 | const innerAccumulator = accumulator(3); 13 | console.log(innerAccumulator(-4)); // -1 14 | console.log(innerAccumulator(1.5)); // 0.5 15 | console.log(innerAccumulator(5)); // 5.5 16 | -------------------------------------------------------------------------------- /src/rosetta-code/ackermann-function.js: -------------------------------------------------------------------------------- 1 | // The Ackermann function is a classic example of a recursive function, 2 | // notable especially because it is not a primitive recursive function. 3 | // It grows very quickly in value, as does the size of its call tree. 4 | // Its arguments are never negative and it always terminates. 5 | // Write a function which returns the value of A(m,n). 6 | 7 | function ack (m, n) { 8 | if (m === 0) return n + 1; 9 | if (m > 0 && n === 0) return ack(m - 1, 1); 10 | if (m > 0 && n > 0) return ack(m - 1, ack(m, n - 1)); 11 | } 12 | 13 | console.time('no cache'); 14 | console.log(ack(3, 10)); // 8189 15 | console.timeEnd('no cache'); 16 | 17 | function memAck (m, n, cache = {}) { 18 | if (typeof cache[`${m}/${n}`] !== 'undefined') return cache[`${m}/${n}`]; 19 | if (m === 0) cache[`${m}/${n}`] = n + 1; 20 | if (m > 0 && n === 0) cache[`${m}/${n}`] = memAck(m - 1, 1, cache); 21 | if (m > 0 && n > 0) cache[`${m}/${n}`] = memAck(m - 1, memAck(m, n - 1, cache), cache); 22 | 23 | return cache[`${m}/${n}`]; 24 | } 25 | 26 | console.time('cache'); 27 | console.log(memAck(3, 10)); // 8189 28 | console.timeEnd('cache'); 29 | -------------------------------------------------------------------------------- /src/rosetta-code/align-columns.js: -------------------------------------------------------------------------------- 1 | // Given a text file of many lines, where fields within a line are delineated 2 | // by a single 'dollar' character, write a program that aligns each column of 3 | // fields by ensuring that words in each column are separated by at least one 4 | // space. Further, allow for each word in a column to be either left justified, 5 | // right justified, or center justified within its column. 6 | // Note that: 7 | // The example input texts lines may, or may not, have trailing dollar characters. 8 | // All columns should share the same alignment. 9 | // Consecutive space characters produced adjacent to the end of lines are 10 | // insignificant for the purposes of the task. 11 | // Output text will be viewed in a mono-spaced font on a plain text editor or basic terminal. 12 | // The minimum space between columns should be computed from the text and not hard-coded. 13 | // It is not a requirement to add separating characters between or around columns. 14 | 15 | function formatText (input, justification = 'center') { 16 | let data; 17 | 18 | if (typeof input === 'string') data = input.split('\n'); 19 | else if (input instanceof Array) data = [...input]; 20 | else throw Error('Input must be an array or string'); 21 | 22 | data = data.map(item => item.split('$').filter(item => item.length !== 0)); 23 | 24 | const maxLengths = data 25 | .reduce((acc, item) => { 26 | item.forEach((word, index) => { 27 | acc[index] = acc[index] || []; 28 | acc[index].push(word); 29 | }); 30 | return acc; 31 | }, []) 32 | .reduce((acc, item) => { 33 | acc.push(item.reduce((maxLen, word) => Math.max(maxLen, word.length), 0)); 34 | return acc; 35 | }, []); 36 | 37 | const result = data.map(item => item.map((word, index) => { 38 | const diff = maxLengths[index] - word.length; 39 | let str; 40 | 41 | if (justification === 'left') str = `${word}${' '.repeat(diff)}`; 42 | if (justification === 'right') str = `${' '.repeat(diff)}${word}`; 43 | if (justification === 'center') { 44 | str = `${' '.repeat(Math.floor(diff / 2))}${word}${' '.repeat(Math.ceil(diff / 2))}`; 45 | } 46 | 47 | return str; 48 | })); 49 | 50 | return result.map(item => item.join(' ')).join('\n'); 51 | } 52 | 53 | const testArr = [ 54 | 'Given$a$text$file$of$many$lines,$where$fields$within$a$line$', 55 | "are$delineated$by$a$single$'dollar'$character,$write$a$program", 56 | 'that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$', 57 | 'column$are$separated$by$at$least$one$space.', 58 | 'Further,$allow$for$each$word$in$a$column$to$be$either$left$', 59 | 'justified,$right$justified,$or$center$justified$within$its$column.' 60 | ]; 61 | 62 | const testStr = `Given$a$text$file$of$many$lines,$where$fields$within$a$line$ 63 | are$delineated$by$a$single$'dollar'$character,$write$a$program 64 | that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ 65 | column$are$separated$by$at$least$one$space. 66 | Further,$allow$for$each$word$in$a$column$to$be$either$left$ 67 | justified,$right$justified,$or$center$justified$within$its$column.`; 68 | 69 | console.log(formatText(testArr)); 70 | // Given a text file of many lines, where fields within a line 71 | // are delineated by a single 'dollar' character, write a program 72 | // that aligns each column of fields by ensuring that words in each 73 | // column are separated by at least one space. 74 | // Further, allow for each word in a column to be either left 75 | // justified, right justified, or center justified within its column. 76 | 77 | console.log(formatText(testStr, 'left')); 78 | // Given a text file of many lines, where fields within a line 79 | // are delineated by a single 'dollar' character, write a program 80 | // that aligns each column of fields by ensuring that words in each 81 | // column are separated by at least one space. 82 | // Further, allow for each word in a column to be either left 83 | // justified, right justified, or center justified within its column. 84 | 85 | console.log(formatText(testArr, 'right')); 86 | // Given a text file of many lines, where fields within a line 87 | // are delineated by a single 'dollar' character, write a program 88 | // that aligns each column of fields by ensuring that words in each 89 | // column are separated by at least one space. 90 | // Further, allow for each word in a column to be either left 91 | // justified, right justified, or center justified within its column. 92 | -------------------------------------------------------------------------------- /src/rosetta-code/amicable-pairs.js: -------------------------------------------------------------------------------- 1 | // Two integers N and M are said to be amicable pairs if N ≠ M and the sum of 2 | // the proper divisors of N (sum(propDivs(N))) = M as well as sum(propDivs(M)) = N. 3 | // Task: Calculate and show here the Amicable pairs below 20,000 (there are eight). 4 | 5 | function amicablePairsUpTo (maxNum) { 6 | const divisorsSum = {}; 7 | const result = []; 8 | let sum, root; 9 | 10 | for (let i = 1; i <= maxNum; i++) { 11 | sum = 1; 12 | root = Math.sqrt(i); 13 | 14 | for (let j = 2; j <= root; j++) { 15 | if (i % j === 0) sum += i / j === j ? j : j + i / j; 16 | } 17 | 18 | if (divisorsSum[sum] === i) result.push([sum, i]); 19 | divisorsSum[i] = sum; 20 | } 21 | 22 | return result; 23 | } 24 | 25 | console.log(amicablePairsUpTo(20000)); 26 | // [ [ 220, 284 ], [ 1184, 1210 ], [ 2620, 2924 ], [ 5020, 5564 ], [ 6232, 6368 ], [ 10744, 10856 ], 27 | // [ 12285, 14595 ], [ 17296, 18416 ] ] 28 | -------------------------------------------------------------------------------- /src/rosetta-code/averages-mode.js: -------------------------------------------------------------------------------- 1 | // Write a program to find the mode value of a collection. 2 | // The case where the collection is empty may be ignored. 3 | // Care must be taken to handle the case where the mode is non-unique. 4 | // If it is not appropriate or possible to support a general collection, 5 | // use a vector (array), if possible. If it is not appropriate or possible 6 | // to support an unspecified value type, use integers. 7 | 8 | function mode (arr) { 9 | const counter = new Map(); 10 | const counterArray = []; 11 | 12 | arr.forEach(item => { 13 | const count = counter.get(item); 14 | if (count) counter.set(item, count + 1); 15 | else counter.set(item, 1); 16 | }); 17 | 18 | counter.forEach((value, key) => counterArray.push([key, value])); 19 | const maxFreq = counterArray.reduce((acc, item) => Math.max(acc, item[1]), 0); 20 | 21 | return counterArray.filter(item => item[1] === maxFreq).map(item => item[0]); 22 | } 23 | 24 | console.log(mode([1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17])); // [ 6 ] 25 | console.log(mode([1, 2, 4, 4, 1])); // [ 1, 4 ] 26 | console.log(mode([true, false, true, false, true, true])); // [ true ] 27 | -------------------------------------------------------------------------------- /src/rosetta-code/averages-pythagorean-means.js: -------------------------------------------------------------------------------- 1 | // Compute all three of the Pythagorean means of the set of integers 1 through 10 (inclusive). 2 | // Show that A(x1,…,xn)≥G(x1,…,xn)≥H(x1,…,xn) for this set of positive integers. 3 | // The most common of the three means, the arithmetic mean, is the sum of the list 4 | // divided by its length: A(x1,…,xn) = (x1 + ⋯ + xn)/n. The geometric mean is the nth root 5 | // of the product of the list: G(x1,…,xn) = Math.pow(x1 * ⋯ * xn, 1/n). The harmonic mean is 6 | // n divided by the sum of the reciprocal of each item in the list: H(x1,…,xn)=n/(1/x1 + ⋯ + 1/xn) 7 | // Assume the input is an ordered array of all inclusive numbers. 8 | // For the answer, please output an object in the following format: 9 | // { values: { arithmetic: 5.5, geometric: 4.528728688116765, harmonic: 3.414171521474055 }, 10 | // test: 'is A >= G >= H ? yes' } 11 | 12 | function pythagoreanMeans (rangeArr) { 13 | const arithmetic = rangeArr.reduce((acc, item) => acc + item, 0) / rangeArr.length; 14 | const geometric = Math.pow(rangeArr.reduce((acc, item) => acc * item, 1), 1 / rangeArr.length); 15 | const harmonic = rangeArr.length / rangeArr.reduce((acc, item) => acc + 1 / item, 0); 16 | 17 | return { 18 | values: { arithmetic, geometric, harmonic }, 19 | test: arithmetic >= geometric && geometric >= harmonic 20 | ? 'is A >= G >= H ? yes' : 'is A >= G >= H ? no' 21 | }; 22 | } 23 | 24 | console.log(pythagoreanMeans([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); 25 | // { values: 26 | // { arithmetic: 5.5, 27 | // geometric: 4.528728688116765, 28 | // harmonic: 3.414171521474055 }, 29 | // test: 'is A >= G >= H ? yes' } 30 | -------------------------------------------------------------------------------- /src/rosetta-code/averages-root-mean-square.js: -------------------------------------------------------------------------------- 1 | // Compute the Root mean square of the numbers 1 through 10 inclusive. 2 | // The root mean square is also known by its initials RMS (or rms), and as the quadratic mean. 3 | // The RMS is calculated as the mean of the squares of the numbers, square-rooted: 4 | // xrms = Math.sqrt((x1 * x1 + x2 * x2+ ⋯ + xn *xn) / n) 5 | 6 | function rms (arr) { 7 | return Math.sqrt(arr.reduce((acc, item) => acc + Math.pow(item, 2), 0) / arr.length); 8 | } 9 | 10 | console.log(rms([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); // 6.2048368229954285 11 | -------------------------------------------------------------------------------- /src/rosetta-code/babbage-problem.js: -------------------------------------------------------------------------------- 1 | // Charles Babbage, looking ahead to the sorts of problems his 2 | // Analytical Engine would be able to solve, gave this example: 3 | // What is the smallest positive integer whose square ends in the digits 269,696? 4 | // He thought the answer might be 99,736, whose square is 9,947,269,696; but he couldn't be certain. 5 | // The task is to find out if Babbage had the right answer. 6 | // Implement a function to return the lowest integer that satisfies the Babbage problem. 7 | 8 | function babbage() { 9 | const endDigits = 269696; 10 | let num = Math.ceil(Math.sqrt(endDigits)); 11 | 12 | for (; num * num % 1000000 !== endDigits; num += 2) {} 13 | 14 | return num; 15 | } 16 | 17 | console.log(babbage()); // 25264 18 | -------------------------------------------------------------------------------- /src/rosetta-code/balanced-brackets.js: -------------------------------------------------------------------------------- 1 | // Determine whether a generated string of brackets is balanced; 2 | // that is, whether it consists entirely of pairs of opening/closing 3 | // brackets (in that order), none of which mis-nest. 4 | 5 | function isBalanced (str) { 6 | const stack = []; 7 | const brackets = { 8 | '}': '{', 9 | ')': '(', 10 | ']': '[' 11 | }; 12 | const bracketsStr = str.replace(/[^(){}[\]]/gi, ''); 13 | 14 | for (const bracket of bracketsStr) { 15 | if (Object.values(brackets).includes(bracket)) { 16 | stack.push(bracket); 17 | } else if (Object.keys(brackets).includes(bracket)) { 18 | const openBracket = stack.pop(); 19 | if (brackets[bracket] !== openBracket) return false; 20 | } else { 21 | throw new Error('Invalid brackets'); 22 | } 23 | } 24 | 25 | if (stack.length) return false; 26 | return true; 27 | } 28 | 29 | console.log(isBalanced('[1{2{fk(f/k)(f[ff])}(adsa)}hjkl]')); // true 30 | console.log(isBalanced('[12{fkf/k(fff})}(adsa)hjkl]')); // false 31 | -------------------------------------------------------------------------------- /src/rosetta-code/combinations.js: -------------------------------------------------------------------------------- 1 | // Given non-negative integers m and n, generate all size m 2 | // combinations of the integers from 0 (zero) to n-1 in sorted 3 | // order (each combination is sorted and the entire table is sorted). 4 | 5 | function combinations (m, n) { 6 | const array = Array.from({ length: n }, (e, i) => i); 7 | const combinations = []; 8 | 9 | function getCombinations(index, arr) { 10 | if (arr.length === m) return combinations.push([...arr]); 11 | 12 | for (let j = index + 1; j < array.length; j++) { 13 | getCombinations(j, [...arr, array[j]]); 14 | } 15 | } 16 | 17 | for (let i = 0; i <= n - m; i++) { 18 | getCombinations(i, [array[i]]); 19 | } 20 | 21 | return combinations; 22 | } 23 | 24 | console.log(combinations(3, 5)); 25 | // [ [ 0, 1, 2 ], [ 0, 1, 3 ], [ 0, 1, 4 ], [ 0, 2, 3 ], [ 0, 2, 4 ], [ 0, 3, 4 ], [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 3, 4 ], [ 2, 3, 4 ] ] 26 | -------------------------------------------------------------------------------- /src/rosetta-code/comma-quibbling.js: -------------------------------------------------------------------------------- 1 | // Write a function to generate a string output which is 2 | // the concatenation of input words from a list/sequence where: 3 | // An input of no words produces the output string of just the two brace characters "{}". 4 | // An input of just one word, e.g. ["ABC"], produces the output string 5 | // of the word inside the two braces, e.g. "{ABC}". 6 | // An input of two words, e.g. ["ABC", "DEF"], produces the output string of the two words 7 | // inside the two braces with the words separated by the string " and ", e.g. "{ABC and DEF}". 8 | // An input of three or more words, e.g. ["ABC", "DEF", "G", "H"], produces 9 | // the output string of all but the last word separated by ", " with the last 10 | // word separated by " and " and all within braces; e.g. "{ABC, DEF, G and H}". 11 | // Note: Assume words are non-empty strings of uppercase characters for this task. 12 | 13 | function quibble (words) { 14 | const len = words.length; 15 | let result = '}'; 16 | 17 | for (let i = len - 1; i >= 0; i--) { 18 | if (i === 0) result = `${words[i]}${result}`; 19 | else if (i === len - 1) result = ` and ${words[i]}${result}`; 20 | else result = `,${words[i]}${result}`; 21 | } 22 | 23 | result = `{${result}`; 24 | 25 | return result; 26 | } 27 | 28 | console.log(quibble([])); // {} 29 | console.log(quibble(['ABC'])); // {ABC} 30 | console.log(quibble(['ABC', 'DEF'])); // {ABC and DEF} 31 | console.log(quibble(['ABC', 'DEF', 'G', 'H'])); // {ABC,DEF,G and H} 32 | -------------------------------------------------------------------------------- /src/rosetta-code/compare-a-list-of-strings.js: -------------------------------------------------------------------------------- 1 | // Given a list of arbitrarily many strings, implement 2 | // a function for each of the following conditions: 3 | // test if they are all lexically equal 4 | // test if every string is lexically less than the one after it 5 | // (i.e. whether the list is in strict ascending order) 6 | 7 | function allEqual (arr) { 8 | return arr.reduce((bool, item, index) => { 9 | if (index === arr.length - 1) return bool; 10 | return bool && item === arr[index + 1]; 11 | }, true); 12 | } 13 | 14 | function azSorted (arr) { 15 | return arr.reduce((bool, item, index) => { 16 | if (index === arr.length - 1) return bool; 17 | return bool && item < arr[index + 1]; 18 | }, true); 19 | } 20 | 21 | console.log(allEqual(['AA', 'AA', 'AA', 'AA'])); // true 22 | console.log(allEqual(['AA', 'ACB', 'BB', 'CC'])); // false 23 | console.log(azSorted(['AA', 'ACB', 'BB', 'CC'])); // true 24 | console.log(azSorted(['AA', 'AA', 'AA', 'AA'])); // false 25 | -------------------------------------------------------------------------------- /src/rosetta-code/convert-seconds-to-compound-duration.js: -------------------------------------------------------------------------------- 1 | // Implement a function which takes a positive integer representing 2 | // a duration in seconds as input (e.g., 100), and returns a string 3 | // which shows the same duration decomposed into weeks, days, hours, 4 | // minutes, and seconds as detailed below (e.g., "1 min, 40 sec"). 5 | 6 | function convertSeconds (sec) { 7 | const timeCollections = { 8 | sec: 1, 9 | min: 60, 10 | hr: 60 * 60, 11 | d: 24 * 60 * 60, 12 | wk: 7 * 24 * 60 * 60 13 | }; 14 | const abbrs = ['wk', 'd', 'hr', 'min', 'sec']; 15 | 16 | return abbrs.reduce((duration, item) => { 17 | const value = Math.floor(sec / timeCollections[item]); 18 | sec -= value * timeCollections[item]; 19 | 20 | if (value) duration.push(`${value} ${item}`); 21 | 22 | return duration; 23 | }, []).join(', '); 24 | } 25 | 26 | console.log(convertSeconds(7259)); // 2 hr, 59 sec 27 | console.log(convertSeconds(86400)); // 1 d 28 | console.log(convertSeconds(600000)); // 9 wk, 6 d, 10 hr, 40 min 29 | -------------------------------------------------------------------------------- /src/rosetta-code/count-occurrences-of-a-substring.js: -------------------------------------------------------------------------------- 1 | // Create a function, or show a built-in function, to count the number 2 | // of non-overlapping occurrences of a substring inside a string. 3 | // The function should take two arguments: the first argument being the string 4 | // to search, and the second a substring to be searched for. 5 | // It should return an integer count. 6 | // The matching should yield the highest number of non-overlapping matches. 7 | // In general, this essentially means matching from left-to-right or right-to-left. 8 | 9 | function countSubstring (str, subStr) { 10 | return str.split(subStr).length - 1; 11 | } 12 | 13 | console.log(countSubstring('the three truths', 'th')); // 3 14 | console.log(countSubstring('ababababab', 'abab')); // 2 15 | console.log(countSubstring('abaabba*bbaba*bbab', 'a*b')); // 2 16 | -------------------------------------------------------------------------------- /src/rosetta-code/count-the-coins.js: -------------------------------------------------------------------------------- 1 | // There are four types of common coins in US currency: quarters (25 cents), 2 | // dimes (10 cents), nickels (5 cents), and pennies (1 cent). 3 | // Implement a function to determine how many ways there are to make change 4 | // for a dollar using these common coins? (1 dollar = 100 cents). 5 | 6 | function countCoinsTD () { 7 | const coins = [1, 5, 10, 25]; 8 | const amount = 100; 9 | const index = coins.length - 1; 10 | const memo = {}; 11 | 12 | function countVariations(amount, index) { 13 | if (amount < 0 || index < 0) return 0; 14 | if (amount === 0) return 1; 15 | 16 | const key = `${index}/${amount}`; 17 | 18 | if (memo[key]) return memo[key]; 19 | 20 | memo[key] = countVariations(amount - coins[index], index) + countVariations(amount, index - 1); 21 | 22 | return memo[key]; 23 | } 24 | 25 | return countVariations(amount, index); 26 | } 27 | 28 | console.log(countCoinsTD()); // 242 29 | 30 | function countCoinsBU () { 31 | const coins = [1, 5, 10, 25]; 32 | const amount = 100; 33 | const table = Array.from({ length: amount + 1 }).fill(0); 34 | table[0] = 1; 35 | 36 | for (let i = 0; i < coins.length; i++) { 37 | for (let j = coins[i]; j <= amount; j++) { 38 | table[j] += table[j - coins[i]]; 39 | } 40 | } 41 | 42 | return table[amount]; 43 | } 44 | 45 | console.log(countCoinsBU()); // 242 46 | -------------------------------------------------------------------------------- /src/rosetta-code/date-format.js: -------------------------------------------------------------------------------- 1 | // Return an array with the current date in the formats: 2 | // - 2007-11-23 and 3 | // - Sunday, November 23, 2007 4 | // Example output: ['2007-11-23', 'Sunday, November 23, 2007'] 5 | 6 | function getDateFormats () { 7 | const daysCollection = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 8 | const monthsCollection = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 9 | const result = []; 10 | const date = new Date(); 11 | const year = date.getFullYear(); 12 | const month = date.getMonth(); 13 | const day = date.getDate(); 14 | 15 | result[0] = `${year}-${month + 1}-${day}`; 16 | result[1] = `${daysCollection[date.getDay()]}, ${monthsCollection[month]} ${day}, ${year}`; 17 | 18 | return result; 19 | } 20 | 21 | console.log(getDateFormats()); // [ '2019-5-29', 'Wednesday, May 29, 2019' ] 22 | 23 | function getDateFormatsWithZero () { 24 | const daysCollection = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 25 | const monthsCollection = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 26 | const result = []; 27 | const date = new Date(); 28 | const year = date.getFullYear(); 29 | const month = date.getMonth(); 30 | const day = ('0' + date.getDate()).slice(-2); 31 | 32 | result[0] = `${year}-${('0' + (month + 1)).slice(-2)}-${day}`; 33 | result[1] = `${daysCollection[date.getDay()]}, ${monthsCollection[month]} ${day}, ${year}`; 34 | return result; 35 | } 36 | 37 | console.log(getDateFormatsWithZero()); // [ '2019-05-29', 'Wednesday, May 29, 2019' ] 38 | -------------------------------------------------------------------------------- /src/rosetta-code/day-of-the-week.js: -------------------------------------------------------------------------------- 1 | // A company decides that whenever Xmas falls on a Sunday 2 | // they will give their workers all extra paid holidays so that, 3 | // together with any public holidays, workers will not have to work 4 | // the following week (between the 25th of December and the first of January). 5 | // Write a function that takes a start year and an end year and return 6 | // an array of all the years where the 25th of December will be a Sunday. 7 | 8 | function findXmasSunday (start, end) { 9 | const result = []; 10 | 11 | for (let year = start; year <= end; year++) { 12 | if (new Date(year, 11, 25).getDay() === 0) result.push(year); 13 | } 14 | 15 | return result; 16 | } 17 | 18 | console.log(findXmasSunday(2008, 2121)); 19 | // [ 2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118 ] 20 | console.log(findXmasSunday(1977, 2016)); // [ 1977, 1983, 1988, 1994, 2005, 2011, 2016 ] 21 | -------------------------------------------------------------------------------- /src/rosetta-code/deepcopy.js: -------------------------------------------------------------------------------- 1 | // Write a function that returns a deep copy of a given object. 2 | // The copy must not be the same object that was given. 3 | // This task will not test for: 4 | // Objects with properties that are functions 5 | // Date objects or object with properties that are Date objects 6 | // RegEx or object with properties that are RegEx objects 7 | 8 | function deepcopy (obj) { 9 | return JSON.parse(JSON.stringify(obj)); 10 | } 11 | 12 | const obj = { 13 | o: { 14 | a: ['an', 'array'], 15 | t: 'test' 16 | }, 17 | t: 'try' 18 | }; 19 | console.log(deepcopy(obj)); // { o: { a: [ 'an', 'array' ], t: 'test' }, t: 'try' } 20 | -------------------------------------------------------------------------------- /src/rosetta-code/define-a-primitive-data-type.js: -------------------------------------------------------------------------------- 1 | // Define a type that behaves like an integer but has 2 | // a lowest valid value of 1 and a highest valid value of 10. 3 | // Errors: 4 | // If you try to instantiate a Num with a value outside of 1 - 10 5 | // it should throw a TypeError with an error message of 'Out of range'. 6 | // If you try to instantiate a Num with a value that is not a number 7 | // it should throw a TypeError with an error message of 'Not a Number'. 8 | 9 | class Num { 10 | constructor(n) { 11 | if (typeof n !== 'number' || isNaN(n)) throw new TypeError('Not a Number'); 12 | n = Math.floor(n); 13 | if (n < 1 || n > 10) throw new TypeError('Out of range'); 14 | this.number = Math.floor(n); 15 | } 16 | 17 | valueOf() { 18 | return this.number; 19 | } 20 | 21 | toString() { 22 | return `${this.number}`; 23 | } 24 | } 25 | 26 | console.log(new Num(3) * new Num(4)); // 12 27 | console.log(new Num(3.5) + new Num(1.8)); // 4 28 | console.log(new Num('error')); // TypeError: Not a Number 29 | console.log(new Num(11)); // TypeError: Out of range 30 | -------------------------------------------------------------------------------- /src/rosetta-code/emirp-primes.js: -------------------------------------------------------------------------------- 1 | // An emirp (prime spelled backwards) are primes that when reversed 2 | // (in their decimal representation) are a different prime. 3 | // Write a function that should be able to : Show the first n eprimes numbers. 4 | // Show the eprimes numbers in a range. Show the number of eprimes in a range. 5 | // Show the nth eprimes number. The function should have two parameters. 6 | // The first will receive n or the range as an array. 7 | // The second will receive a boolean, that specifies if the function returns the eprimes 8 | // as an array or a single number (the number of primes in the range or the nth prime). 9 | // According to the parameters the function should return an array or a number. 10 | 11 | function isPrime(num) { 12 | if (num < 2) return false; 13 | if (num === 2) return true; 14 | if (num % 2 === 0) return false; 15 | 16 | for (let i = 3; i <= Math.sqrt(num); i += 2) { 17 | if (num % i === 0) return false; 18 | } 19 | 20 | return true; 21 | } 22 | 23 | function emirps(n, isReturningSeq) { 24 | const seq = []; 25 | const isArray = n instanceof Array; 26 | const start = isArray ? n[0] : 13; 27 | const end = isArray ? n[1] : n; 28 | let reversedNumber; 29 | 30 | for (let i = start; ; i++) { 31 | if (isPrime(i)) { 32 | reversedNumber = parseInt(`${i}`.split('').reverse().join(''), 10); 33 | 34 | if (i !== reversedNumber && isPrime(reversedNumber)) seq.push(i); 35 | } 36 | 37 | if ((isArray && i > end) || (!isArray && seq.length >= end)) break; 38 | } 39 | 40 | return isReturningSeq === undefined ? seq[seq.length - 1] : isReturningSeq ? seq : seq.length; 41 | } 42 | 43 | console.log(emirps(20, true)); // [ 13, 17, 31, 37, 71, 73, 79, 97, 107, 113, 149, 157, 167, 179, 199, 311, 337, 347, 359, 389 ] 44 | console.log(emirps(10000)); // 948349 45 | console.log(emirps([7700, 8000], true)); // [ 7717, 7757, 7817, 7841, 7867, 7879, 7901, 7927, 7949, 7951, 7963 ] 46 | console.log(emirps([7700, 8000], false)); // 11 47 | -------------------------------------------------------------------------------- /src/rosetta-code/entropy.js: -------------------------------------------------------------------------------- 1 | // Calculate the Shannon entropy H of a given input string. 2 | 3 | function entropy (s) { 4 | return Object.values(s.split('').reduce((acc, item) => { 5 | acc[item] = ++acc[item] || 1; 6 | return acc; 7 | }, {})).reduce((acc, item) => { 8 | acc += item / s.length * Math.log2(item / s.length); 9 | return acc; 10 | }, 0) * -1; 11 | } 12 | 13 | console.log(entropy('0123456789abcdef')); // 4 14 | console.log(entropy('1223334444')); // 1.8464393446710154 15 | -------------------------------------------------------------------------------- /src/rosetta-code/equilibrium-index.js: -------------------------------------------------------------------------------- 1 | // An equilibrium index of a sequence is an index into the sequence such that 2 | // the sum of elements at lower indices is equal to the sum of elements at higher indices. 3 | // For example, in a sequence A: A0=−7 A1=1 A2=5 A3=2 A4=−4 A5=3 A6=0 4 | // 3 is an equilibrium index, because: A0+A1+A2 = A4+A5+A6 5 | // 6 is also an equilibrium index, because: A0+A1+A2+A3+A4+A5 = 0 (sum of zero elements is zero) 6 | // 7 is not an equilibrium index, because it is not a valid index of sequence A. 7 | // Write a function that, given a sequence, returns its equilibrium indices (if any). 8 | // Assume that the sequence may be very long. 9 | 10 | function equilibrium (a) { 11 | const result = []; 12 | let left = 0; 13 | let right = a.slice(1).reduce((acc, item) => acc + item, 0); 14 | 15 | for (let i = 0; i < a.length; i++) { 16 | if (left === right) result.push(i); 17 | 18 | left += a[i]; 19 | right -= a[i + 1]; 20 | } 21 | 22 | return result; 23 | } 24 | 25 | console.log(equilibrium([-7, 1, 5, 2, -4, 3, 0])); // [ 3, 6 ] 26 | console.log(equilibrium([1, -1, 1, -1, 1, -1, 1])); // [ 0, 1, 2, 3, 4, 5, 6 ] 27 | console.log(equilibrium([1])); // [ 0 ] 28 | console.log(equilibrium([])); // [] 29 | -------------------------------------------------------------------------------- /src/rosetta-code/ethiopian-multiplication.js: -------------------------------------------------------------------------------- 1 | // Ethiopian multiplication is a method of multiplying integers using only 2 | // addition, doubling, and halving. Method: 3 | // Take two numbers to be multiplied and write them down at the top of two columns. 4 | // In the left-hand column repeatedly halve the last number, discarding any remainders, 5 | // and write the result below the last in the same column, until you write a value of 1. 6 | // In the right-hand column repeatedly double the last number and write the result below. 7 | // Stop when you add a result in the same row as where the left hand column shows 1. 8 | // Examine the table produced and discard any row where the value in the left column is even. 9 | // Sum the values in the right-hand column that remain to produce the result 10 | // of multiplying the original two numbers together. 11 | // Task: The task is to define three named functions/methods/procedures/subroutines: 12 | // one to halve an integer, one to double an integer, and one to state if an integer is even. 13 | // Use these functions to create a function that does Ethiopian multiplication. 14 | 15 | function halve (num) { 16 | return Math.floor(num / 2); 17 | } 18 | 19 | function double (num) { 20 | return num * 2; 21 | } 22 | 23 | function isEven (num) { 24 | return num % 2 === 0; 25 | } 26 | 27 | function ethMult (a, b) { 28 | const column = [{ a, b }]; 29 | 30 | while (a !== 1) { 31 | a = halve(a); 32 | b = double(b); 33 | column.push({ a, b }); 34 | } 35 | 36 | return column.filter(item => !isEven(item.a)).reduce((acc, item) => acc + item.b, 0); 37 | } 38 | 39 | console.log(ethMult(23, 46)); // 1058 40 | console.log(ethMult(56, 98)); // 5488 41 | -------------------------------------------------------------------------------- /src/rosetta-code/evaluate-binomial-coefficients.js: -------------------------------------------------------------------------------- 1 | // Write a function to calculate the binomial coefficient for the given value of n and k. 2 | // This formula is recommended: (nk) = n!/((n−k)!k!) = n(n−1)(n−2)…(n−k+1)/k(k−1)(k−2)…1 3 | 4 | function binom (n, k) { 5 | if (k < 0 || k > n) return 0; 6 | 7 | let factorialN = 1; 8 | let factorialK; 9 | let factorialNK; 10 | 11 | for (let i = 1; i <= n; i++) { 12 | factorialN *= i; 13 | 14 | if (i === k) factorialK = factorialN; 15 | if (i === n - k) factorialNK = factorialN; 16 | } 17 | 18 | return factorialN / (factorialNK * factorialK); 19 | } 20 | 21 | console.log(binom(12, 8)); // 495 22 | console.log(binom(6, 1)); // 6 23 | console.log(binom(10, 4)); // 210 24 | -------------------------------------------------------------------------------- /src/rosetta-code/factorial.js: -------------------------------------------------------------------------------- 1 | // Write a function to return the factorial of a number. 2 | 3 | function factorial(num) { 4 | if (num < 0) throw new Error('The number must not be negative'); 5 | 6 | let result = 1; 7 | 8 | for (let i = 2; i <= num; i++) { 9 | result *= i; 10 | } 11 | 12 | return result; 13 | } 14 | 15 | console.log(factorial(20)); // 2432902008176640000 16 | -------------------------------------------------------------------------------- /src/rosetta-code/factors-of-an-integer.js: -------------------------------------------------------------------------------- 1 | // Write a function that returns the factors of a positive integer. 2 | // These factors are the positive integers by which the number 3 | // being factored can be divided to yield a positive integer result. 4 | 5 | function factors (num) { 6 | const resultLeft = []; 7 | const resultRight = []; 8 | const squareRoot = Math.sqrt(num); 9 | 10 | for (let i = 1; i <= squareRoot; i++) { 11 | if (num % i === 0) { 12 | resultLeft.push(i); 13 | if (num / i !== i) resultRight.unshift(num / i); 14 | } 15 | } 16 | 17 | return [...resultLeft, ...resultRight]; 18 | } 19 | 20 | console.log(factors(45)); // [ 1, 3, 5, 9, 15, 45 ] 21 | console.log(factors(64)); // [ 1, 2, 4, 8, 16, 32, 64 ] 22 | -------------------------------------------------------------------------------- /src/rosetta-code/farey-sequence.js: -------------------------------------------------------------------------------- 1 | // The Farey sequence Fn of order n is the sequence of completely reduced 2 | // fractions between 0 and 1 which, when in lowest terms, have denominators 3 | // less than or equal to n, arranged in order of increasing size. 4 | // Each Farey sequence: starts with the value 0, denoted by the fraction 0/1 5 | // and ends with the value 1, denoted by the fraction 1/1. 6 | // Tasks: Write a function that returns the Farey sequence of order n. 7 | // The function should have one parameter that is n. It should return the sequence as an array. 8 | // Compute and show the Farey sequence for orders 1 through 11 (inclusive). 9 | // Compute and display the number of fractions in the Farey sequence for order 10 | // 100 through 1,000 (inclusive) by hundreds. Show the fractions as n/d 11 | // (using the solidus [or slash] to separate the numerator from the denominator). 12 | 13 | function gcd(a, b) { 14 | while (a && b) { 15 | a %= b; 16 | b = a ? b % a : b; 17 | } 18 | 19 | return a || b; 20 | } 21 | 22 | function farey (n) { 23 | const seq = []; 24 | 25 | for (let i = 1; i < n; i++) { 26 | for (let j = i + 1; j <= n; j++) { 27 | if (gcd(i, j) === 1) seq.push({ s: `${i}/${j}`, n: i / j }); 28 | } 29 | } 30 | 31 | return ['0/1', ...seq.sort((a, b) => a.n - b.n).map(item => item.s), '1/1']; 32 | } 33 | 34 | console.log(farey(5)); // [ '0/1','1/5','1/4','1/3','2/5','1/2','3/5','2/3','3/4','4/5','1/1' ] 35 | 36 | function getSolutionsToTasks () { 37 | return { 38 | seq11: Array(11).fill().map((e, i) => farey(i + 1)), 39 | len: Array(1000 / 100).fill().map((e, i) => farey((i + 1) * 100).length) 40 | }; 41 | } 42 | 43 | console.log(getSolutionsToTasks()); 44 | // { seq11: 45 | // [ [ '0/1', '1/1' ], 46 | // [ '0/1', '1/2', '1/1' ], 47 | // [ '0/1', '1/3', '1/2', '2/3', '1/1' ], 48 | // [ '0/1', '1/4', '1/3', '1/2', '2/3', '3/4', '1/1' ], 49 | // [ '0/1', '1/5', '1/4', '1/3', '2/5', '1/2', '3/5', '2/3', '3/4', '4/5', '1/1' ], 50 | // [ '0/1', '1/6', '1/5', '1/4', '1/3', '2/5', '1/2', '3/5', '2/3', '3/4', '4/5', '5/6', '1/1' ], 51 | // [ '0/1', '1/7', '1/6', '1/5', '1/4', '2/7', '1/3', '2/5', '3/7', '1/2', '4/7', '3/5', '2/3', '5/7', '3/4', '4/5', '5/6', '6/7', '1/1' ], 52 | // [ '0/1', '1/8', '1/7', '1/6', '1/5', '1/4', '2/7', '1/3', '3/8', '2/5', '3/7', '1/2', '4/7', '3/5', '5/8', '2/3', '5/7', '3/4', '4/5', '5/6', '6/7', '7/8', '1/1' ], 53 | // [ '0/1', '1/9', '1/8', '1/7', '1/6', '1/5', '2/9', '1/4', '2/7', '1/3', '3/8', '2/5', '3/7', '4/9', '1/2', '5/9', '4/7', '3/5', '5/8', '2/3', '5/7', '3/4', '7/9', '4/5', '5/6', '6/7', '7/8', '8/9', '1/1' ], 54 | // [ '0/1', '1/10', '1/9', '1/8', '1/7', '1/6', '1/5', '2/9', '1/4', '2/7', '3/10', '1/3', '3/8', '2/5', '3/7', '4/9', '1/2', '5/9', '4/7', '3/5', '5/8', '2/3', '7/10', '5/7', '3/4', '7/9', '4/5', '5/6', '6/7', '7/8', '8/9', '9/10', '1/1' ], 55 | // [ '0/1', '1/11', '1/10', '1/9', '1/8', '1/7', '1/6', '2/11', '1/5', '2/9', '1/4', '3/11', '2/7', '3/10', '1/3', '4/11', '3/8', '2/5', '3/7', '4/9', '5/11', '1/2', '6/11', '5/9', '4/7', '3/5', '5/8', '7/11', '2/3', '7/10', '5/7', '8/11', '3/4', '7/9', '4/5', '9/11', '5/6', '6/7', '7/8', '8/9', '9/10', '10/11', '1/1' ] ], 56 | // len: [ 3045, 12233, 27399, 48679, 76117, 109501, 149019, 194751, 246327, 304193 ] } 57 | -------------------------------------------------------------------------------- /src/rosetta-code/fibonacci-n-step-number-sequences.js: -------------------------------------------------------------------------------- 1 | // Write a function to generate Fibonacci n-step number sequences and 2 | // Lucas sequences. The first parameter will be n. The second parameter 3 | // will be the number of elements to be returned. The third parameter 4 | // will specify whether to output the Fibonacci sequence or the Lucas sequence. 5 | // If the parameter is "f" then return the Fibonacci sequence and if it is "l", 6 | // then return the Lucas sequence. The sequences must be returned as an array. 7 | // The Lucas series uses [2,1] as its initial values. 8 | 9 | function fibLuc (n, len, w) { 10 | const seq = w === 'f' ? [1, 1] : [2, 1]; 11 | let sum = seq[0]; 12 | 13 | for (let i = 1; i < n; i++) { 14 | sum += seq[i]; 15 | seq.push(sum); 16 | } 17 | 18 | for (let i = n; i < len - 1; i++) { 19 | sum = sum + seq[i] - seq[i - n]; 20 | seq.push(sum); 21 | } 22 | 23 | return seq; 24 | } 25 | 26 | console.log(fibLuc(4, 15, 'f')); // [ 1, 1, 2, 4, 8, 15, 29, 56, 108, 208, 401, 773, 1490, 2872, 5536 ] 27 | console.log(fibLuc(3, 15, 'l')); // [ 2, 1, 3, 6, 10, 19, 35, 64, 118, 217, 399, 734, 1350, 2483, 4567 ] 28 | -------------------------------------------------------------------------------- /src/rosetta-code/fibonacci-sequence.js: -------------------------------------------------------------------------------- 1 | // Write a function to generate the nth Fibonacci number. 2 | 3 | function * genFibSequence () { 4 | let next = 1; 5 | let prev = 0; 6 | 7 | while (true) { 8 | yield prev; 9 | [prev, next] = [next, prev + next]; 10 | } 11 | } 12 | 13 | function fibonacci (n, isReturningSeq = false) { 14 | if (n <= 0) return isReturningSeq ? [0] : 0; 15 | 16 | const genFib = genFibSequence(); 17 | const result = []; 18 | let count = 1; 19 | 20 | while (count <= n) { 21 | result.push(genFib.next().value); 22 | count++; 23 | } 24 | 25 | return isReturningSeq ? result : result[result.length - 1]; 26 | } 27 | 28 | console.log(fibonacci(10, true)); // [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ] 29 | console.log(fibonacci(73)); // 498454011879264 30 | -------------------------------------------------------------------------------- /src/rosetta-code/fibonacci-word.js: -------------------------------------------------------------------------------- 1 | // Write a function to return the Fibonacci Words upto N. N will be provided 2 | // as a parameter to the function. The function should return an array of objects. 3 | // The objects should be of the form : { number: 1, length: 1, entropy: 0, word: '1' }. 4 | // The Fibonacci Word may be created in a manner analogous to 5 | // the Fibonacci Sequence as described here: Define F_Word1 as 1, Define F_Word2 as 0 6 | // Form F_Word3 as F_Word2 concatenated with F_Word1 i.e.: 01 7 | // Form F_Wordn as F_Wordn-1 concatenated with F_wordn-2 8 | 9 | function * genFibWordSequence () { 10 | let next = '0'; 11 | let prev = '1'; 12 | 13 | while (true) { 14 | yield prev; 15 | [prev, next] = [next, next + prev]; 16 | } 17 | } 18 | 19 | function entropy (s) { 20 | return Object.values(s.split('').reduce((acc, item) => { 21 | acc[item] = ++acc[item] || 1; 22 | return acc; 23 | }, {})).reduce((acc, item) => { 24 | acc += item / s.length * Math.log2(item / s.length); 25 | return acc; 26 | }, 0) * -1; 27 | } 28 | 29 | function fibWord (n) { 30 | const genFibWord = genFibWordSequence(); 31 | const result = []; 32 | let count = 1; 33 | let word; 34 | 35 | while (count <= n) { 36 | word = genFibWord.next().value; 37 | result.push({ 38 | number: count, 39 | length: word.length, 40 | entropy: entropy(word), 41 | word: word.length < 100 ? word : '' 42 | }); 43 | count++; 44 | } 45 | 46 | return result; 47 | } 48 | 49 | console.log(fibWord(7)); 50 | // [ { number: 1, length: 1, entropy: -0, word: '1' }, 51 | // { number: 2, length: 1, entropy: -0, word: '0' }, 52 | // { number: 3, length: 2, entropy: 1, word: '01' }, 53 | // { number: 4, length: 3, entropy: 0.9182958340544896, word: '010' }, 54 | // { number: 5, length: 5, entropy: 0.9709505944546686, word: '01001' }, 55 | // { number: 6, length: 8, entropy: 0.954434002924965, word: '01001010' }, 56 | // { number: 7, length: 13, entropy: 0.9612366047228759, word: '0100101001001' } ] 57 | -------------------------------------------------------------------------------- /src/rosetta-code/general-fizzbuzz.js: -------------------------------------------------------------------------------- 1 | // Write a generalized version of FizzBuzz that works for any list 2 | // of factors, along with their words. This is basically a "fizzbuzz" 3 | // implementation where the rules of the game are supplied to the user. 4 | // Create a function to implement this. The function should take two parameters. 5 | // The first will be an array with the FizzBuzz rules. 6 | // For example: [ [3,"Fizz"] , [5,"Buzz"] ]. 7 | // This indcates that Fizz should be printed if the number is a multiple of 3 8 | // and Buzz if it is a multiple of 5. If it is a multiple of both then 9 | // the strings should be concatenated in the order specified in the array. 10 | // In this case, FizzBuzz if the number is a multiple of 3 and 5. 11 | // The second parameter is the number for which the function should 12 | // return a string as stated above. 13 | 14 | function genFizzBuzz (rules, num) { 15 | const numbers = Array.from({ length: num }, (e, i) => i + 1); 16 | 17 | return numbers.map(item => { 18 | return rules.reduce((str, rule) => { 19 | if (item % rule[0] === 0) str += rule[1]; 20 | return str; 21 | }, '') || item; 22 | }).join('\n'); 23 | } 24 | 25 | console.log(genFizzBuzz([ 26 | [3, 'Fizz'], 27 | [5, 'Buzz'], 28 | [7, 'Baxx'] 29 | ], 20)); 30 | 31 | // 1 32 | // 2 33 | // Fizz 34 | // 4 35 | // Buzz 36 | // Fizz 37 | // Baxx 38 | // 8 39 | // Fizz 40 | // Buzz 41 | // 11 42 | // Fizz 43 | // 13 44 | // Baxx 45 | // FizzBuzz 46 | // 16 47 | // 17 48 | // Fizz 49 | // 19 50 | // Buzz 51 | -------------------------------------------------------------------------------- /src/rosetta-code/generate-lower-case-ASCII-alphabet.js: -------------------------------------------------------------------------------- 1 | // Write a function to generate an array of lower case ASCII characters, 2 | // for a given range. For example: for range 1 to 4 the function 3 | // should return ['a','b','c','d']. 4 | 5 | function lascii (cFrom, cTo) { 6 | const result = [cFrom]; 7 | 8 | for (let i = cFrom.charCodeAt(0) + 1; i <= cTo.charCodeAt(0); i++) { 9 | result.push(String.fromCharCode(i)); 10 | } 11 | 12 | return result; 13 | } 14 | 15 | console.log(lascii('c', 'i')); // [ 'c', 'd', 'e', 'f', 'g', 'h', 'i' ] 16 | -------------------------------------------------------------------------------- /src/rosetta-code/generator-exponential.js: -------------------------------------------------------------------------------- 1 | // Write a function that uses generators to generate squares and cubes. 2 | // Create a new generator that filters all cubes from the generator of squares. 3 | // The function should return the nth value of the filtered generator. 4 | // For example for n=7, the function should return 81 as the sequence 5 | // would be 4,9,16,25,36,49,81. Here 64 is filtered out, as it is a cube. 6 | 7 | function * genSequence (power) { 8 | let count = 1; 9 | while (++count) { 10 | yield Math.pow(count, power); 11 | } 12 | } 13 | 14 | function exponentialGenerator (n) { 15 | const result = []; 16 | const genSquares = genSequence(2); 17 | const genCubes = genSequence(3); 18 | let square; 19 | let cube = genCubes.next().value; 20 | 21 | while (result.length !== n) { 22 | square = genSquares.next().value; 23 | if (cube < square) cube = genCubes.next().value; 24 | if (cube !== square) result.push(square); 25 | } 26 | 27 | return result[result.length - 1]; 28 | } 29 | 30 | console.log(exponentialGenerator(10)); // 144 31 | console.log(exponentialGenerator(25)); // 784 32 | -------------------------------------------------------------------------------- /src/rosetta-code/gray-code.js: -------------------------------------------------------------------------------- 1 | // Gray code is a form of binary encoding where transitions between 2 | // consecutive numbers differ by only one bit. 3 | // Create a function to encode a number to and decode a number 4 | // from Gray code. The function should will have 2 parameters. 5 | // The first would be a boolean. The function should encode for true and 6 | // decode for false. The second parameter would be the number to be encoded/decoded. 7 | 8 | function gray(enc, number) { 9 | if (enc) return number ^ (number >> 1); 10 | 11 | let decodedNumber = number; 12 | 13 | while (number !== 1) { 14 | number = number >> 1; 15 | decodedNumber = decodedNumber ^ number; 16 | } 17 | 18 | return decodedNumber; 19 | } 20 | 21 | console.log(gray(true, 177)); // 233 22 | console.log(gray(true, 425)); // 381 23 | console.log(gray(false, 233)); // 177 24 | console.log(gray(false, 381)); // 425 25 | -------------------------------------------------------------------------------- /src/rosetta-code/greatest-common-divisor.js: -------------------------------------------------------------------------------- 1 | // Write a function that returns the greatest common divisor of two integers. 2 | 3 | function gcd(a, b) { 4 | while (a && b) { 5 | a %= b; 6 | b = a ? b % a : b; 7 | } 8 | 9 | return Math.abs(a || b); 10 | } 11 | console.log(gcd(-1300, 250)); // 50 12 | console.log(gcd(111, 13)); // 1 13 | console.log(gcd(1000, 25)); // 25 14 | console.log(gcd(111, 15)); // 3 15 | -------------------------------------------------------------------------------- /src/rosetta-code/greatest-subsequential-sum.js: -------------------------------------------------------------------------------- 1 | // Given a sequence of integers, find a continuous subsequence 2 | // which maximizes the sum of its elements, that is, the elements 3 | // of no other single subsequence add up to a value larger than this one. 4 | // An empty subsequence is considered to have the sum of 0; 5 | // thus if all elements are negative, the result must be the empty sequence. 6 | 7 | function maximumSubsequence (population) { 8 | let result = []; 9 | let max = 0; 10 | let subsequence; 11 | let subSum; 12 | 13 | for (let i = 0; i < population.length; i++) { 14 | subsequence = []; 15 | subSum = 0; 16 | for (let j = i; j < population.length; j++) { 17 | subSum += population[j]; 18 | subsequence.push(population[j]); 19 | 20 | if (subSum > max) { 21 | max = subSum; 22 | result = [...subsequence]; 23 | } 24 | } 25 | } 26 | 27 | return result; 28 | } 29 | 30 | console.log(maximumSubsequence([-1, -2, 3, 5, 6, -2, -1, 4, -4, 2, -1])); // [ 3, 5, 6, -2, -1, 4 ] 31 | console.log(maximumSubsequence([1, 2, 3, 4, 5, -8, -9, -20, 40, 25, -5])); // [ 40, 25 ] 32 | console.log(maximumSubsequence([])); // [] 33 | console.log(maximumSubsequence([-1, -2, -3, -5, -6, -2, -1, -4, -4, -2, -1])); // [] 34 | -------------------------------------------------------------------------------- /src/rosetta-code/hailstone-sequence.js: -------------------------------------------------------------------------------- 1 | // The Hailstone sequence of numbers can be generated from a starting positive integer, n by: 2 | // If n is 1 then the sequence ends. If n is even then the next n of the sequence = n/2 3 | // If n is odd then the next n of the sequence = (3 * n) + 1 4 | // Task: Create a routine to generate the hailstone sequence for a number. 5 | // Use the routine to show that the hailstone sequence for the number 27 has 112 6 | // elements starting with 27, 82, 41, 124 and ending with 8, 4, 2, 1 7 | // Show the number less than 100,000 which has the longest hailstone sequence 8 | // together with that sequence's length. (But don't show the actual sequence!) 9 | 10 | function hailstoneSequence (num) { 11 | const result = [num]; 12 | 13 | while (num > 1) { 14 | num = num % 2 === 0 ? num / 2 : (3 * num) + 1; 15 | result.push(num); 16 | } 17 | 18 | return result; 19 | } 20 | 21 | function getSolutionsToTasks () { 22 | return [hailstoneSequence(27).filter((item, index, arr) => index < 4 || index > arr.length - 5), 23 | Array(100000).fill().map((e, i) => hailstoneSequence(i + 1).length).reduce((acc, item, index) => 24 | item > acc[0] ? [item, index + 1] : acc, [0])]; 25 | } 26 | 27 | console.log(getSolutionsToTasks()); // [ [ 27, 82, 41, 124, 8, 4, 2, 1 ], [ 351, 77031 ] ] 28 | -------------------------------------------------------------------------------- /src/rosetta-code/happy-numbers.js: -------------------------------------------------------------------------------- 1 | // A happy number is defined by the following process: 2 | // Starting with any positive integer, replace the number by the sum 3 | // of the squares of its digits, and repeat the process until the number 4 | // equals 1 (where it will stay), or it loops endlessly in a cycle 5 | // which does not include 1. Those numbers for which this process ends 6 | // in 1 are happy numbers, while those that do not end in 1 are unhappy numbers. 7 | // Implement a function that returns true if the number is happy, or false if not. 8 | // Implement a function that finds and prints the first 8 happy numbers. 9 | 10 | function happy (num) { 11 | let n = num; 12 | const numbers = {}; 13 | 14 | while (true) { 15 | n = `${n}`.split('').reduce((acc, item) => acc + Math.pow(parseInt(item, 10), 2), 0); 16 | 17 | if (n === 1) return true; 18 | if (numbers[n]) return false; 19 | 20 | numbers[n] = true; 21 | } 22 | } 23 | 24 | console.log(happy(32)); // true 25 | console.log(happy(33)); // false 26 | 27 | function sequenceOfHappyNumbers(len) { 28 | const result = []; 29 | let count = 0; 30 | 31 | while (result.length < len) { 32 | if (happy(++count)) result.push(count); 33 | } 34 | 35 | return result; 36 | } 37 | 38 | console.log(sequenceOfHappyNumbers(8)); // [ 1, 7, 10, 13, 19, 23, 28, 31 ] 39 | -------------------------------------------------------------------------------- /src/rosetta-code/harshad-or-niven-series.js: -------------------------------------------------------------------------------- 1 | // The Harshad or Niven numbers are positive integers ≥ 1 that are 2 | // divisible by the sum of their digits. For example, 42 is a Harshad number 3 | // as 42 is divisible by (4 + 2) without remainder. 4 | // Assume that the series is defined as the numbers in increasing order. 5 | // Implement a function to generate successive members of the Harshad sequence. 6 | // Use it to list the first twenty members of the sequence and 7 | // list the first Harshad number greater than 1000. 8 | 9 | function findSequence (n, start) { 10 | const result = []; 11 | let count = start; 12 | 13 | while (result.length < n) { 14 | const digitsSum = `${count}`.split('').reduce((acc, item) => acc + parseInt(item), 0); 15 | 16 | if (count % digitsSum === 0) result.push(count); 17 | 18 | count++; 19 | } 20 | 21 | return result; 22 | } 23 | 24 | function isHarshadOrNiven () { 25 | return { 26 | firstTwenty: findSequence(20, 1), 27 | firstOver1000: findSequence(1, 1001)[0] 28 | }; 29 | } 30 | 31 | console.log(isHarshadOrNiven()); 32 | // { firstTwenty: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 18, 20, 21, 24, 27, 30, 36, 40, 42 ], firstOver1000: 1002 } 33 | -------------------------------------------------------------------------------- /src/rosetta-code/hash-from-two-arrays.js: -------------------------------------------------------------------------------- 1 | // Using two Arrays of equal length, create a Hash object where the elements 2 | // from one array (the keys) are linked to the elements of the other (the values) 3 | 4 | function arrToObj (keys, vals) { 5 | return keys.reduce((acc, item, index) => { 6 | acc[item] = vals[index]; 7 | return acc; 8 | }, {}); 9 | } 10 | 11 | console.log(arrToObj([1, 2, 3, 4, 5], ['a', 'b', 'c', 'd', 'e'])); // { '1': 'a', '2': 'b', '3': 'c', '4': 'd', '5': 'e' } 12 | console.log(arrToObj([1, 2, 3, 4, 5], ['a', 'b', 'c', 'd'])); // { '1': 'a', '2': 'b', '3': 'c', '4': 'd', '5': undefined } 13 | console.log(arrToObj(['a', 'b', 'c'], [1, 2, 3, 4, 5])); // { a: 1, b: 2, c: 3 } 14 | -------------------------------------------------------------------------------- /src/rosetta-code/heronian-triangles.js: -------------------------------------------------------------------------------- 1 | // Hero's formula for the area of a triangle given the length of its three sides 2 | // a, b, and c is given by: A = Math.sqrt(s*(s−a)*(s−b)*(s−c)), 3 | // where s is half the perimeter of the triangle; that is s = (a+b+c)/2. 4 | // Heronian triangles are triangles whose sides and area are all integers. 5 | // An example is the triangle with sides 3, 4, 5 whose area is 6 (and whose perimeter is 12). 6 | // Note that any triangle whose sides are all an integer multiple of 3, 4, 5; 7 | // such as 6, 8, 10, will also be a Heronian triangle. 8 | // Define a Primitive Heronian triangle as a Heronian triangle where the greatest common divisor 9 | // of all three sides is 1 (unity). This will exclude, for example, triangle 6, 8, 10. 10 | // Task: 11 | // Create a named function/method/procedure/... that implements Hero's formula. 12 | // Use the function to generate all the primitive Heronian triangles with sides <= 200. 13 | // Show the count of how many triangles are found. 14 | // Order the triangles by first increasing area, then by increasing perimeter, 15 | // then by increasing maximum side lengths. 16 | // Show the first ten ordered triangles in a table of sides, perimeter, and area. 17 | // Show a similar ordered table for those triangles with area = 210. 18 | 19 | function heronianTriangle (triangle) { 20 | const perimeter = triangle.reduce((acc, item) => acc + item, 0); 21 | const a = triangle.reduce((acc, item) => 22 | acc * (perimeter / 2 - item), perimeter / 2); 23 | 24 | return a <= 0 ? null : Math.sqrt(a) % 1 === 0 25 | ? { area: Math.sqrt(a), perimeter, triangle } : null; 26 | } 27 | 28 | function gcd(a, b) { 29 | while (a && b) { 30 | a %= b; 31 | b = a ? b % a : b; 32 | } 33 | 34 | return a || b; 35 | } 36 | 37 | function generateHeronianTriangles (num) { 38 | const result = []; 39 | 40 | for (let a = 1; a <= num; a++) { 41 | for (let b = 1; b <= a; b++) { 42 | for (let c = 1; c <= b; c++) { 43 | const triangle = heronianTriangle([c, b, a]); 44 | if (triangle && gcd(gcd(a, b), c) === 1) { 45 | result.push(triangle); 46 | } 47 | } 48 | } 49 | } 50 | 51 | return result; 52 | } 53 | 54 | function sortByAreaPerimeterMaxSide (a, b) { 55 | if (a.area > b.area) return 1; 56 | if (a.area < b.area) return -1; 57 | if (a.perimeter > b.perimeter) return 1; 58 | if (a.perimeter < b.perimeter) return -1; 59 | 60 | return a.triangle.reduce((acc, item) => 61 | item > acc ? item : acc, 0) - b.triangle.reduce((acc, item) => 62 | item > acc ? item : acc, 0); 63 | } 64 | 65 | function getSolutionsToTasks (num) { 66 | const triangles = generateHeronianTriangles(num); 67 | 68 | return { 69 | trianglesCount: triangles.length, 70 | tenSortedTriangles: [...triangles].sort(sortByAreaPerimeterMaxSide).slice(0, 10), 71 | area210: triangles.filter(item => item.area === 210).sort(sortByAreaPerimeterMaxSide) 72 | }; 73 | } 74 | 75 | console.log(getSolutionsToTasks(200)); 76 | // { trianglesCount: 517, 77 | // tenSortedTriangles: 78 | // [ { area: 6, perimeter: 12, triangle: [ 3, 4, 5 ] }, 79 | // { area: 12, perimeter: 16, triangle: [ 5, 5, 6 ] }, 80 | // { area: 12, perimeter: 18, triangle: [ 5, 5, 8 ] }, 81 | // { area: 24, perimeter: 32, triangle: [ 4, 13, 15 ] }, 82 | // { area: 30, perimeter: 30, triangle: [ 5, 12, 13 ] }, 83 | // { area: 36, perimeter: 36, triangle: [ 9, 10, 17 ] }, 84 | // { area: 36, perimeter: 54, triangle: [ 3, 25, 26 ] }, 85 | // { area: 42, perimeter: 42, triangle: [ 7, 15, 20 ] }, 86 | // { area: 60, perimeter: 36, triangle: [ 10, 13, 13 ] }, 87 | // { area: 60, perimeter: 40, triangle: [ 8, 15, 17 ] } ], 88 | // area210: 89 | // [ { area: 210, perimeter: 70, triangle: [ 17, 25, 28 ] }, 90 | // { area: 210, perimeter: 70, triangle: [ 20, 21, 29 ] }, 91 | // { area: 210, perimeter: 84, triangle: [ 12, 35, 37 ] }, 92 | // { area: 210, perimeter: 84, triangle: [ 17, 28, 39 ] }, 93 | // { area: 210, perimeter: 140, triangle: [ 7, 65, 68 ] }, 94 | // { area: 210, perimeter: 300, triangle: [ 3, 148, 149 ] } ] } 95 | -------------------------------------------------------------------------------- /src/rosetta-code/hofstadter-Q-sequence.js: -------------------------------------------------------------------------------- 1 | // The Hofstadter Q sequence is defined as: 2 | // Q(1)=Q(2)=1, Q(n)=Q(n−Q(n−1))+Q(n−Q(n−2)), n>2. 3 | // It is defined like the Fibonacci sequence, but whereas the next term 4 | // in the Fibonacci sequence is the sum of the previous two terms, 5 | // in the Q sequence the previous two terms tell you how far to go back 6 | // in the Q sequence to find the two numbers to sum to make the next term of the sequence. 7 | // Task: 1.Implement the Hofstadter Q Sequence equation into JavaScript. 8 | // 2.Count and display how many times a member of the sequence is less 9 | // than its preceding term for terms up to and including the 100,000th term. 10 | 11 | // Top-down approach - Memoization 12 | function memoize(fn) { 13 | const cache = {}; 14 | 15 | return function(arg) { 16 | if (cache[arg] !== undefined) return cache[arg]; 17 | 18 | cache[arg] = fn.call(this, arg); 19 | return cache[arg]; 20 | }; 21 | } 22 | 23 | const memHofstadterQ = memoize(hofstadterQ); 24 | 25 | function hofstadterQ (n) { 26 | if (n <= 2) return 1; 27 | return memHofstadterQ(n - memHofstadterQ(n - 1)) + memHofstadterQ(n - memHofstadterQ(n - 2)); 28 | } 29 | 30 | console.log(hofstadterQ(1000)); // 502 31 | console.log(hofstadterQ(2500)); // 1261 32 | // console.log(hofstadterQ(100000)); // RangeError: Maximum call stack size exceeded 33 | 34 | // Bottom-up approach - Tabulation 35 | 36 | function hofstadterQBU(n) { 37 | const result = [0, 1, 1]; 38 | 39 | for (let i = 3; i <= n; i++) { 40 | result[i] = result[i - result[i - 1]] + result[i - result[i - 2]]; 41 | } 42 | 43 | return { 44 | lastTerm: result[n], 45 | numberOfLessTerms: result.reduce((acc, item, idx, arr) => item < arr[idx - 1] ? acc + 1 : acc, 0) 46 | }; 47 | } 48 | 49 | console.log(hofstadterQBU(2500)); // { lastTerm: 1261, numberOfLessTerms: 1159 } 50 | console.log(hofstadterQBU(100000)); // { lastTerm: 48157, numberOfLessTerms: 49798 } 51 | console.log(hofstadterQBU(1000000)); // { lastTerm: 512066, numberOfLessTerms: 498638 } 52 | -------------------------------------------------------------------------------- /src/rosetta-code/hofstadter-figure-figure-sequences.js: -------------------------------------------------------------------------------- 1 | // These two sequences of positive integers are defined as: 2 | // R(1)=1, S(1)=2R(n)=R(n−1)+S(n−1), n>1. 3 | // The sequence S(n) is further defined as the sequence of positive 4 | // integers not present in R(n). 5 | // Sequence R starts: 1, 3, 7, 12, 18, ... 6 | // Sequence S starts: 2, 4, 5, 6, 8, ... 7 | // Task: Create two functions named ffr and ffs that when given n return R(n) 8 | // or S(n) respectively.(Note that R(1) = 1 and S(1) = 2 to avoid off-by-one errors). 9 | // No maximum value for n should be assumed. 10 | 11 | function getSequences(n) { 12 | const r = [0, 1]; 13 | const s = [0, 2]; 14 | const objR = { 1: true }; 15 | 16 | for (let i = 2; i <= n; i++) { 17 | r[i] = r[i - 1] + s[i - 1]; 18 | objR[r[i]] = true; 19 | s[i] = s[i - 1] + 1; 20 | if (objR[s[i]]) s[i]++; 21 | } 22 | 23 | return { r, s }; 24 | } 25 | 26 | function ffr(n) { 27 | return getSequences(n).r[n]; 28 | } 29 | 30 | function ffs(n) { 31 | return getSequences(n).s[n]; 32 | } 33 | 34 | console.log(ffr(1000)); // 526334 35 | console.log(ffs(1000)); // 1041 36 | -------------------------------------------------------------------------------- /src/rosetta-code/i-before-e-except-after-c.js: -------------------------------------------------------------------------------- 1 | // The phrase "I before E, except after C" is a widely known mnemonic 2 | // which is supposed to help when spelling English words. 3 | // Using the words provided, check if the two sub-clauses 4 | // of the phrase are plausible individually: 5 | // "I before E when not preceded by C". 6 | // "E before I when preceded by C". 7 | // If both sub-phrases are plausible then the original phrase 8 | // can be said to be plausible. 9 | // Write a function that accepts a word and check if the word follows this rule. 10 | // The function should return true if it follows the rule otherwise false. 11 | 12 | function IBeforeExceptC (word) { 13 | if (word.includes('cei')) return true; 14 | 15 | const index = word.indexOf('ie'); 16 | if (index !== -1 && word[index - 1] !== 'c') return true; 17 | 18 | return false; 19 | } 20 | 21 | console.log(IBeforeExceptC('receive')); // true 22 | console.log(IBeforeExceptC('science')); // false 23 | -------------------------------------------------------------------------------- /src/rosetta-code/iterated-digits-squaring.js: -------------------------------------------------------------------------------- 1 | // If you add the square of the digits of a Natural number 2 | // (an integer bigger than zero), you always end with either 1 or 89: 3 | // 15 -> 26 -> 40 -> 16 -> 37 -> 58 -> 89 4 | // Write a function that takes a number as a parameter and returns 5 | // 1 or 89 after performing the mentioned process. 6 | // Count how many number chains for integers 1 <= n < 1_000_000 7 | // (1 <= n < 100_000_000) end with a value 89. 8 | 9 | function iteratedSquare (n) { 10 | while (true) { 11 | if (n === 1 || n === 89) return n; 12 | n = `${n}`.split('').reduce((acc, item) => acc + Math.pow(parseInt(item), 2), 0); 13 | } 14 | } 15 | 16 | console.log(iteratedSquare(70)); // 1 17 | 18 | function getNumberOfChains () { 19 | const memo = {}; 20 | 21 | function iteratedSquare (n) { 22 | let number = n; 23 | 24 | while (true) { 25 | if (number === 1 || number === 89) { 26 | memo[n] = number; 27 | return number; 28 | } 29 | 30 | if (memo[number]) { 31 | memo[n] = memo[number]; 32 | return memo[number]; 33 | } 34 | 35 | number = `${number}`.split('').reduce((acc, item) => acc + Math.pow(parseInt(item), 2), 0); 36 | } 37 | } 38 | 39 | let count = 0; 40 | 41 | for (let i = 1; i < 1000000; i++) { 42 | if (iteratedSquare(i) === 89) count++; 43 | } 44 | 45 | return count; 46 | } 47 | 48 | console.log(getNumberOfChains()); // 856929 49 | -------------------------------------------------------------------------------- /src/rosetta-code/josephus-problem.js: -------------------------------------------------------------------------------- 1 | // Josephus problem is a math puzzle with a grim description: 2 | // n prisoners are standing on a circle, sequentially numbered from 0 to n−1. 3 | // An executioner walks along the circle, starting from prisoner 0, 4 | // removing every k-th prisoner and killing him. As the process goes on, 5 | // the circle becomes smaller and smaller, until only one prisoner remains, who is then freed. 6 | // Given any n, k > 0, find out which prisoner will be the final survivor. 7 | 8 | function josephus (init, kill) { 9 | let circle = Array.from({ length: init }, (e, i) => i); 10 | let counter = init < kill ? kill % init - 1 : kill - 1; 11 | let length = circle.length; 12 | const removed = []; 13 | 14 | if (kill === 1) return { survivor: circle[length - 1], removed: circle.slice(0, -1) }; 15 | 16 | while (circle.length > 1) { 17 | removed.push(circle[counter]); 18 | circle[counter] = undefined; 19 | 20 | if (counter + kill >= length) { 21 | circle = circle.filter(item => typeof item !== 'undefined'); 22 | counter = counter + kill - length; 23 | length = circle.length; 24 | counter = counter >= length ? counter % length : counter; 25 | } else { 26 | counter += kill; 27 | } 28 | } 29 | 30 | return { survivor: circle[0], removed }; 31 | } 32 | 33 | console.log(josephus(41, 3)); 34 | // { survivor: 30, removed: [2,5,8,11,14,17,20,23,26,29,32,35,38,0,4,9,13,18,22,27,31,36,40,6,12,19,25,33,39,7,16,28,37,10,24,1,21,3,34,15] } 35 | console.log(josephus(15, 1)); // { survivor: 14, removed: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] } 36 | -------------------------------------------------------------------------------- /src/rosetta-code/tokenize-a-string-with-escaping.js: -------------------------------------------------------------------------------- 1 | // Write a function or program that can split a string at each non-escaped 2 | // occurrence of a separator character. It should accept three input parameters: 3 | // the string, the separator character, the escape character. 4 | // It should output a list of strings. Rules for splitting: 5 | // The fields that were separated by the separators, become the elements 6 | // of the output list. Empty fields should be preserved, even at the start and end. 7 | // Rules for escaping: "Escaped" means preceded by an occurrence of the escape 8 | // character that is not already escaped itself. When the escape character precedes 9 | // a character that has no special meaning, it still counts as an escape 10 | // (but does not do anything special). Each occurrences of the escape character 11 | // that was used to escape something, should not become part of the output. 12 | 13 | function tokenize(str, sep, esc) { 14 | const result = []; 15 | let substring = ''; 16 | 17 | for (let i = 0; i < str.length; i++) { 18 | if (str[i] === sep) { 19 | result.push(substring); 20 | substring = ''; 21 | } else if (str[i] === esc) { 22 | substring += str[++i]; 23 | } else { 24 | substring += str[i]; 25 | } 26 | } 27 | 28 | result.push(substring); 29 | return result; 30 | } 31 | 32 | console.log(tokenize('one^|uno||three^^^^|four^^^|^cuatro|', '|', '^')); // [ 'one|uno', '', 'three^^', 'four^|cuatro', '' ] 33 | console.log(tokenize('a@&bcd&ef&&@@hi', '&', '@')); // [ 'a&bcd', 'ef', '', '@hi' ] 34 | -------------------------------------------------------------------------------- /src/rosetta-code/top-rank-per-group.js: -------------------------------------------------------------------------------- 1 | // Find the top N ranked data in each group, where N is provided as a 2 | // parameter. Name of the rank and the group are also provided as parameter. 3 | 4 | function topRankPerGroup(n, data, groupName, rankName) { 5 | if (n < 0) return; 6 | 7 | data.sort((a, b) => { 8 | if (a[groupName] > b[groupName]) return 1; 9 | if (a[groupName] === b[groupName]) return b[rankName] - a[rankName]; 10 | return -1; 11 | } 12 | ); 13 | 14 | const groupedData = []; 15 | 16 | for (let i = 1, j = 0; i < data.length; i++) { 17 | if (data[i][groupName] !== data[j][groupName]) { 18 | groupedData.push(data.slice(j, i)); 19 | j = i; 20 | } 21 | 22 | if (i === data.length - 1) groupedData.push(data.slice(j, i + 1)); 23 | } 24 | 25 | return groupedData.reduce((acc, item) => { 26 | acc.push(item.slice(0, n)); 27 | return acc; 28 | }, []); 29 | } 30 | 31 | const data = [ 32 | { name: 'Tyler Bennett', id: 'E10297', salary: 32000, dept: 'D101' }, 33 | { name: 'John Rappl', id: 'E21437', salary: 47000, dept: 'D050' }, 34 | { name: 'George Woltman', id: 'E00127', salary: 53500, dept: 'D101' }, 35 | { name: 'Adam Smith', id: 'E63535', salary: 18000, dept: 'D202' }, 36 | { name: 'Claire Buckman', id: 'E39876', salary: 27800, dept: 'D202' }, 37 | { name: 'David McClellan', id: 'E04242', salary: 41500, dept: 'D101' }, 38 | { name: 'Rich Holcomb', id: 'E01234', salary: 49500, dept: 'D202' }, 39 | { name: 'Nathan Adams', id: 'E41298', salary: 21900, dept: 'D050' }, 40 | { name: 'Richard Potter', id: 'E43128', salary: 15900, dept: 'D101' }, 41 | { name: 'David Motsinger', id: 'E27002', salary: 19250, dept: 'D202' }, 42 | { name: 'Tim Sampair', id: 'E03033', salary: 27000, dept: 'D101' }, 43 | { name: 'Kim Arlich', id: 'E10001', salary: 57000, dept: 'D190' }, 44 | { name: 'Timothy Grove', id: 'E16398', salary: 29900, dept: 'D190' } 45 | ]; 46 | console.log(topRankPerGroup(2, data, 'dept', 'salary')); 47 | // [ [ { name: 'John Rappl', id: 'E21437', salary: 47000, dept: 'D050' }, 48 | // { name: 'Nathan Adams', id: 'E41298', salary: 21900, dept: 'D050' } ], 49 | // [ { name: 'George Woltman', id: 'E00127', salary: 53500, dept: 'D101' }, 50 | // { name: 'David McClellan', id: 'E04242', salary: 41500, dept: 'D101' } ], 51 | // [ { name: 'Kim Arlich', id: 'E10001', salary: 57000, dept: 'D190' }, 52 | // { name: 'Timothy Grove', id: 'E16398', salary: 29900, dept: 'D190' } ], 53 | // [ { name: 'Rich Holcomb', id: 'E01234', salary: 49500, dept: 'D202' }, 54 | // { name: 'Claire Buckman',id: 'E39876',salary: 27800,dept: 'D202' } ] ] 55 | -------------------------------------------------------------------------------- /src/rosetta-code/topological-sort.js: -------------------------------------------------------------------------------- 1 | // Given a mapping between items, and items they depend on, a topological sort 2 | // orders items so that no item precedes an item it depends upon. 3 | // The compiling of a library in the VHDL language has the constraint that 4 | // a library must be compiled after any library it depends on. 5 | // Task: Write a function that will return a valid compile order of VHDL 6 | // libraries from their dependencies. Assume library names are single words. 7 | // Items mentioned as only dependents have no dependents of their own, 8 | // but their order of compiling must be given. Any self dependencies should be ignored. 9 | // Any un-orderable dependencies should be flagged. 10 | 11 | function topologicalSort(libs) { 12 | const libsObject = libs.split('\n').reduce((acc, item) => { 13 | const libsArray = item.split(' ').filter(i => i !== ''); 14 | acc[libsArray[0]] = libsArray.slice(1).filter(i => i !== libsArray[0]); 15 | return acc; 16 | }, {}); 17 | 18 | const visited = {}; 19 | const libsStack = {}; 20 | const result = []; 21 | 22 | function traverse(lib) { 23 | if (visited[lib]) return; 24 | 25 | visited[lib] = true; 26 | 27 | if (typeof libsObject[lib] === 'undefined') return result.push(lib); 28 | 29 | libsStack[lib] = true; 30 | 31 | for (const item of libsObject[lib]) { 32 | if (libsStack[item]) console.log(`Un-orderable dependency: ${Object.keys(libsStack)}`); 33 | if (!visited[item]) traverse(item); 34 | } 35 | 36 | delete libsStack[lib]; 37 | result.push(lib); 38 | } 39 | 40 | for (const lib of Object.keys(libsObject)) { 41 | traverse(lib); 42 | } 43 | 44 | return result; 45 | } 46 | 47 | const libs1 = 48 | `des_system_lib std synopsys std_cell_lib des_system_lib dw02 dw01 ramlib ieee 49 | dw01 ieee dw01 dware gtech 50 | dw02 ieee dw02 dware 51 | dw03 std synopsys dware dw03 dw02 dw01 ieee gtech 52 | dw04 dw04 ieee dw01 dware gtech 53 | dw05 dw05 ieee dware 54 | dw06 dw06 ieee dware 55 | dw07 ieee dware 56 | dware ieee dware 57 | gtech ieee gtech 58 | ramlib std ieee 59 | std_cell_lib ieee std_cell_lib 60 | synopsys`; 61 | 62 | console.log(topologicalSort(libs1)); 63 | // [ 'std', 64 | // 'synopsys', 65 | // 'ieee', 66 | // 'std_cell_lib', 67 | // 'dware', 68 | // 'dw02', 69 | // 'gtech', 70 | // 'dw01', 71 | // 'ramlib', 72 | // 'des_system_lib', 73 | // 'dw03', 74 | // 'dw04', 75 | // 'dw05', 76 | // 'dw06', 77 | // 'dw07' ] 78 | 79 | const libs2 = 80 | `des_system_lib std synopsys std_cell_lib des_system_lib dw02 dw01 ramlib ieee 81 | dw01 ieee dw01 dware gtech 82 | dw02 ieee dw02 83 | dw03 std synopsys dware dw03 dw02 dw01 ieee gtech 84 | dw04 dw04 ieee dw01 dware gtech 85 | dw05 dw05 ieee dware 86 | dw06 dw06 ieee dware 87 | dw07 ieee dware 88 | dware ieee dware des_system_lib 89 | gtech ieee gtech 90 | ramlib std ieee 91 | std_cell_lib ieee std_cell_lib 92 | synopsys`; 93 | 94 | console.log(topologicalSort(libs2)); 95 | 96 | // Un-orderable dependency: des_system_lib,dw01,dware 97 | // [ 'std', 98 | // 'synopsys', 99 | // 'ieee', 100 | // 'std_cell_lib', 101 | // 'dw02', 102 | // 'dware', 103 | // 'gtech', 104 | // 'dw01', 105 | // 'ramlib', 106 | // 'des_system_lib', 107 | // 'dw03', 108 | // 'dw04', 109 | // 'dw05', 110 | // 'dw06', 111 | // 'dw07' ] 112 | -------------------------------------------------------------------------------- /src/rosetta-code/towers-of-hanoi.js: -------------------------------------------------------------------------------- 1 | // Solve the Towers of Hanoi problem. 2 | // Your solution should accept the number of discs as the first parameters, and 3 | // three string used to identify each of the three stacks of discs, for example 4 | // towerOfHanoi(4, 'A', 'B', 'C'). The function should return an 5 | // array of arrays containing the list of moves, source -> destination. 6 | 7 | function towerOfHanoi (n, a, b, c) { 8 | const result = []; 9 | 10 | function move(n, a, b, c) { 11 | if (n > 0) { 12 | move(n - 1, a, c, b); 13 | result.push([a, b]); 14 | move(n - 1, c, b, a); 15 | } 16 | } 17 | 18 | move(n, a, b, c); 19 | 20 | return result; 21 | } 22 | 23 | console.log(towerOfHanoi(3, 'A', 'B', 'C')); 24 | // [ [ 'A', 'B' ], [ 'A', 'C' ], [ 'B', 'C' ], [ 'A', 'B' ], [ 'C', 'A' ], [ 'C', 'B' ], [ 'A', 'B' ] ] 25 | -------------------------------------------------------------------------------- /src/rosetta-code/vector-cross-product.js: -------------------------------------------------------------------------------- 1 | // A vector is defined as having three dimensions as being represented 2 | // by an ordered collection of three numbers: (X, Y, Z). 3 | // Write a function that takes two vectors (arrays) as input and computes their cross product. 4 | // Your function should return null on invalid inputs (ie vectors of different lengths). 5 | 6 | function crossProduct(vector1, vector2) { 7 | if (!vector1 || 8 | !vector2 || 9 | vector1.length !== vector2.length || 10 | vector1.length !== 3) return null; 11 | 12 | return [(vector1[0] * vector2[1] - vector1[1] * vector2[0]), 13 | -(vector1[0] * vector2[2] - vector1[2] * vector2[0]), 14 | (vector1[1] * vector2[2] - vector1[2] * vector2[1])]; 15 | } 16 | 17 | console.log(crossProduct([1, 2, 3], [4, 5, 6])); // [ -3, 6, -3 ] 18 | console.log(crossProduct()); // null 19 | console.log(crossProduct([1, 2, 3], [4, 5, 6, 4])); // null 20 | -------------------------------------------------------------------------------- /src/rosetta-code/vector-dot-product.js: -------------------------------------------------------------------------------- 1 | // A vector is defined as having three dimensions as being 2 | // represented by an ordered collection of three numbers: (X, Y, Z). 3 | // Write a function that takes any numbers of vectors (arrays) 4 | // as input and computes their dot product. 5 | // Your function should return null on invalid inputs (ie vectors of different lengths). 6 | 7 | function dotProduct(...vectors) { 8 | if (vectors.length < 2) return null; 9 | 10 | const len = vectors[0].length; 11 | 12 | if (!vectors.every(item => item.length === len)) return null; 13 | 14 | let product = 0; 15 | 16 | for (let i = 0; i < len; i++) { 17 | product += vectors.reduce((subProduct, item) => { 18 | subProduct *= item[i]; 19 | return subProduct; 20 | }, 1); 21 | } 22 | 23 | return product; 24 | } 25 | 26 | console.log(dotProduct([1], [1])); // 1 27 | console.log(dotProduct([1, 3, -5], [4, -2, -1])); // 3 28 | console.log(dotProduct([3, 4, 5], [4, 3, 5], [-5, -12, -13])); // -529 29 | console.log(dotProduct()); // null 30 | console.log(dotProduct([1, 2], [1])); // null 31 | -------------------------------------------------------------------------------- /src/searching-algorithms/binary-search.js: -------------------------------------------------------------------------------- 1 | // Binary Search 2 | // Write a function called binarySearch which accepts a sorted array and 3 | // a value and returns the index at which the value exists. Otherwise, return -1. 4 | 5 | // Time Complexity - O(log n) 6 | 7 | function binarySearch(arr, val) { 8 | let left = 0; 9 | let right = arr.length - 1; 10 | 11 | while (left <= right) { 12 | const middle = Math.floor((left + right) / 2); 13 | 14 | if (arr[middle] === val) return middle; 15 | 16 | if (arr[middle] > val) right = middle - 1; 17 | else left = middle + 1; 18 | } 19 | 20 | return -1; 21 | } 22 | 23 | console.log(binarySearch([5, 6, 10, 14, 18, 30, 37, 40, 44, 79, 84, 86, 98, 99], 10)); // 2 24 | console.log(binarySearch([5, 10, 16, 34, 37, 40, 44, 64, 84, 86, 95, 98, 99], 95)); // 10 25 | console.log(binarySearch([5, 6, 13, 14, 18, 64, 79, 84, 86, 95, 96, 98, 99], 100)); // -1 26 | -------------------------------------------------------------------------------- /src/searching-algorithms/knuth-morris-pratt-search.js: -------------------------------------------------------------------------------- 1 | // Knuth-Morris-Pratt algorithm 2 | // Write a function which accepts a string and a pattern, and returns the index 3 | // at which the value exists. If the pattern does not exist in the string, return -1. 4 | 5 | // Time Complexity - O(n + m) 6 | // Space complexity - O(n) 7 | 8 | function buildArrayPattern(pattern) { 9 | const arrayPattern = [0]; 10 | let prefix = 0; 11 | let suffix = 1; 12 | 13 | while (arrayPattern.length < pattern.length) { 14 | if (pattern[prefix] === pattern[suffix]) { 15 | arrayPattern.push(prefix + 1); 16 | prefix++; 17 | suffix++; 18 | } else if (prefix === 0) { 19 | arrayPattern.push(0); 20 | suffix++; 21 | } else { 22 | prefix = arrayPattern[prefix - 1]; 23 | } 24 | } 25 | 26 | return arrayPattern; 27 | } 28 | 29 | function kmp(text, pattern) { 30 | const arrayPattern = buildArrayPattern(pattern); 31 | let textIndex = 0; 32 | let patternIndex = 0; 33 | 34 | while (textIndex < text.length) { 35 | if (text[textIndex] === pattern[patternIndex]) { 36 | if (patternIndex === pattern.length - 1) { 37 | return textIndex - pattern.length + 1; 38 | } else { 39 | textIndex++; 40 | patternIndex++; 41 | } 42 | } else if (patternIndex === 0) { 43 | textIndex++; 44 | } else { 45 | patternIndex = arrayPattern[patternIndex - 1]; 46 | } 47 | } 48 | 49 | return -1; 50 | } 51 | 52 | console.log(kmp('dabcdabcyabkglabcdcxabcdabcdabcy', 'abcdabcy')); // 1 53 | console.log(kmp('dabcdabcyabkglabcdcxabcdabcdabcy', 'abcdabcyd')); // -1 54 | 55 | function kmpCounter(text, pattern) { 56 | const arrayPattern = buildArrayPattern(pattern); 57 | let textIndex = 0; 58 | let patternIndex = 0; 59 | let counter = 0; 60 | 61 | while (textIndex < text.length) { 62 | if (text[textIndex] === pattern[patternIndex]) { 63 | if (patternIndex === pattern.length - 1) { 64 | textIndex++; 65 | patternIndex = 0; 66 | counter++; 67 | } else { 68 | textIndex++; 69 | patternIndex++; 70 | } 71 | } else if (patternIndex === 0) { 72 | textIndex++; 73 | } else { 74 | patternIndex = arrayPattern[patternIndex - 1]; 75 | } 76 | } 77 | 78 | return counter; 79 | } 80 | 81 | console.log(kmpCounter('dabcdabcyabkglabcdcxabcdabcdabcy', 'abcdabcy')); // 2 82 | console.log(kmpCounter('dabcdabcyabkglabcdcxabcdabcdabcy', 'abcdabcyd')); // 0 83 | -------------------------------------------------------------------------------- /src/searching-algorithms/linear-search.js: -------------------------------------------------------------------------------- 1 | // Linear Search 2 | // Write a function called linearSearch which accepts an array and a value, 3 | // and returns the index at which the value exists. 4 | // If the value does not exist in the array, return -1. 5 | // Don't use indexOf to implement this function! 6 | 7 | // Time Complexity - O(n) 8 | 9 | function linearSearch(arr, val) { 10 | for (let i = 0; i < arr.length; i++) { 11 | if (arr[i] === val) return i; 12 | } 13 | 14 | return -1; 15 | } 16 | 17 | console.log(linearSearch([10, 15, 20, 25, 30], 15)); // 1 18 | console.log(linearSearch([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 4)); // 5 19 | console.log(linearSearch([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 10)); // -1 20 | -------------------------------------------------------------------------------- /src/searching-algorithms/naive-string-search.js: -------------------------------------------------------------------------------- 1 | // naive string search 2 | // Write a function which accepts a string and a pattern, 3 | // and counts the number of times the pattern appears in the string. 4 | 5 | // Time Complexity - O(n * m) 6 | 7 | function stringSearch (str, val) { 8 | let count = 0; 9 | 10 | for (let i = 0; i <= str.length - val.length; i++) { 11 | for (let j = 0; j < val.length; j++) { 12 | if (str[i + j] !== val[j]) break; 13 | if (j === val.length - 1) count++; 14 | } 15 | } 16 | 17 | return count; 18 | } 19 | 20 | console.log(stringSearch('hojoklokoklok', 'lok')); // 2 21 | -------------------------------------------------------------------------------- /src/sorting-algorithms/bubble-sort/bubble-sort-comparator.js: -------------------------------------------------------------------------------- 1 | // Bubble Sort Comparator 2 | 3 | // Implement a function called bubbleSort. 4 | // Given an array, bubbleSort will sort the values in the array. 5 | // The function takes 2 parameters: an array and an optional comparator function. 6 | // The comparator function is a callback that will take two values from the array to be compared. 7 | // The function returns a negative value if the first value is less than the second, 8 | // a positive value if the first value is greater than the second, 9 | // and 0 if both values are equal. 10 | 11 | // The default comparator you provide should assume that the two parameters are 12 | // numbers and that we are sorting the values from smallest to largest. 13 | 14 | function bubbleSort(arr, comparator) { 15 | if (typeof comparator !== 'function') { 16 | comparator = (a, b) => { 17 | if (a < b) return -1; 18 | if (a > b) return 1; 19 | return 0; 20 | }; 21 | } 22 | 23 | let swap; 24 | 25 | for (let i = 0; i < arr.length; i++) { 26 | swap = false; 27 | 28 | for (let j = 0; j < arr.length - 1 - i; j++) { 29 | if (comparator(arr[j], arr[j + 1]) > 0) { 30 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; 31 | swap = true; 32 | } 33 | } 34 | 35 | if (!swap) break; 36 | } 37 | 38 | return arr; 39 | } 40 | 41 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 42 | console.log(bubbleSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 43 | 44 | console.log(bubbleSort(['LilBub', 'Garfield', 'Blue', 'Grumpy'], (a, b) => { 45 | if (a[1] < b[1]) return -1; 46 | if (a[1] > b[1]) return 1; 47 | return 0; 48 | })); // [ 'Garfield', 'LilBub', 'Blue', 'Grumpy' ] 49 | 50 | const moarKittyData = [{ 51 | name: 'LilBub', 52 | age: 7 53 | }, { 54 | name: 'Garfield', 55 | age: 40 56 | }, { 57 | name: 'Heathcliff', 58 | age: 45 59 | }, { 60 | name: 'Blue', 61 | age: 1 62 | }, { 63 | name: 'Grumpy', 64 | age: 6 65 | }]; 66 | 67 | console.log(bubbleSort(moarKittyData, (a, b) => b.age - a.age)); // sorted by age in descending order 68 | // [ { name: 'Heathcliff', age: 45 }, 69 | // { name: 'Garfield', age: 40 }, 70 | // { name: 'LilBub', age: 7 }, 71 | // { name: 'Grumpy', age: 6 }, 72 | // { name: 'Blue', age: 1 } ] 73 | -------------------------------------------------------------------------------- /src/sorting-algorithms/bubble-sort/bubble-sort.js: -------------------------------------------------------------------------------- 1 | // Bubble Sort 2 | // Bubble sort is an O(n^2) algorithm. 3 | 4 | function bubbleSort(arr) { 5 | let swap; 6 | 7 | for (let i = 0; i < arr.length; i++) { 8 | swap = false; 9 | 10 | for (let j = 0; j < arr.length - 1 - i; j++) { 11 | if (arr[j] > arr[j + 1]) { 12 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; 13 | swap = true; 14 | } 15 | } 16 | 17 | if (!swap) break; 18 | } 19 | 20 | return arr; 21 | } 22 | 23 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 24 | console.log(bubbleSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 25 | console.log(bubbleSort([0, -10, 7, 4])); // [-10, 0, 4, 7] 26 | -------------------------------------------------------------------------------- /src/sorting-algorithms/insertion-sort/insertion-sort-comparator.js: -------------------------------------------------------------------------------- 1 | // Insertion Sort Comparator 2 | 3 | // Implement insertionSort. 4 | // Given an array, insertionSort will sort the values in the array. 5 | // The function take 2 parameters: an array and an optional comparator function. 6 | // The comparator function is a callback that will take two values from the array to be compared. 7 | // The function returns a negative value if the first value is less than the second, 8 | // a positive value if the first value is greater than the second, 9 | // and 0 if both values are equal. The default comparator you provide should assume 10 | // that the two parameters are numbers and that we are sorting the values from smallest to largest. 11 | 12 | // Using swap 13 | function insertionSortSwap(arr, comparator) { 14 | if (typeof comparator !== 'function') { 15 | comparator = (a, b) => { 16 | if (a < b) return -1; 17 | if (a > b) return 1; 18 | return 0; 19 | }; 20 | } 21 | 22 | for (let i = 0; i < arr.length - 1; i++) { 23 | for (let j = i + 1; j > 0; j--) { 24 | if (comparator(arr[j], arr[j - 1]) < 0) [arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]; 25 | else break; 26 | } 27 | } 28 | 29 | return arr; 30 | } 31 | 32 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 33 | console.log(insertionSortSwap(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 34 | 35 | console.log(insertionSortSwap(['LilBub', 'Garfield', 'Blue', 'Grumpy'], (a, b) => { 36 | if (a[1] < b[1]) return -1; 37 | if (a[1] > b[1]) return 1; 38 | return 0; 39 | })); // [ 'Garfield', 'LilBub', 'Blue', 'Grumpy' ] 40 | 41 | const moarKittyData = [{ 42 | name: 'LilBub', 43 | age: 7 44 | }, { 45 | name: 'Garfield', 46 | age: 40 47 | }, { 48 | name: 'Heathcliff', 49 | age: 45 50 | }, { 51 | name: 'Blue', 52 | age: 1 53 | }, { 54 | name: 'Grumpy', 55 | age: 6 56 | }]; 57 | 58 | console.log(insertionSortSwap(moarKittyData, (a, b) => b.age - a.age)); // sorted by age in descending order 59 | // [ { name: 'Heathcliff', age: 45 }, 60 | // { name: 'Garfield', age: 40 }, 61 | // { name: 'LilBub', age: 7 }, 62 | // { name: 'Grumpy', age: 6 }, 63 | // { name: 'Blue', age: 1 } ] 64 | 65 | // Using auxiliary variable 66 | function insertionSortVariable(arr, comparator) { 67 | if (typeof comparator !== 'function') { 68 | comparator = (a, b) => { 69 | if (a < b) return -1; 70 | if (a > b) return 1; 71 | return 0; 72 | }; 73 | } 74 | 75 | for (let i = 1; i < arr.length; i++) { 76 | const current = arr[i]; 77 | let j; 78 | 79 | for (j = i - 1; j >= 0 && comparator(current, arr[j]) < 0; j--) { 80 | arr[j + 1] = arr[j]; 81 | } 82 | 83 | arr[j + 1] = current; 84 | } 85 | 86 | return arr; 87 | } 88 | 89 | console.log(insertionSortVariable(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 90 | 91 | console.log(insertionSortVariable(['LilBub', 'Garfield', 'Blue', 'Grumpy'], (a, b) => { 92 | if (a[1] < b[1]) return -1; 93 | if (a[1] > b[1]) return 1; 94 | return 0; 95 | })); // [ 'Garfield', 'LilBub', 'Blue', 'Grumpy' ] 96 | 97 | console.log(insertionSortVariable(moarKittyData, (a, b) => b.age - a.age)); // sorted by age in descending order 98 | // [ { name: 'Heathcliff', age: 45 }, 99 | // { name: 'Garfield', age: 40 }, 100 | // { name: 'LilBub', age: 7 }, 101 | // { name: 'Grumpy', age: 6 }, 102 | // { name: 'Blue', age: 1 } ] 103 | -------------------------------------------------------------------------------- /src/sorting-algorithms/insertion-sort/insertion-sort.js: -------------------------------------------------------------------------------- 1 | // Insertion Sort 2 | // Insertion sort is an O(n^2) algorithm. 3 | 4 | // Using swap 5 | function insertionSortSwap(arr) { 6 | for (let i = 0; i < arr.length - 1; i++) { 7 | for (let j = i + 1; j > 0; j--) { 8 | if (arr[j] < arr[j - 1]) [arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]; 9 | else break; 10 | } 11 | } 12 | 13 | return arr; 14 | } 15 | 16 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 17 | console.log(insertionSortSwap(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 18 | console.log(insertionSortSwap([0, -10, 7, 4])); // [-10, 0, 4, 7] 19 | 20 | // Using auxiliary variable 21 | function insertionSortVariable(arr) { 22 | for (let i = 1; i < arr.length; i++) { 23 | const current = arr[i]; 24 | let j; 25 | 26 | for (j = i - 1; j >= 0 && arr[j] > current; j--) { 27 | arr[j + 1] = arr[j]; 28 | } 29 | 30 | arr[j + 1] = current; 31 | } 32 | 33 | return arr; 34 | } 35 | 36 | console.log(insertionSortVariable(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 37 | console.log(insertionSortVariable([0, -10, 7, 4])); // [-10, 0, 4, 7] 38 | -------------------------------------------------------------------------------- /src/sorting-algorithms/merge-sort/merge-sort-comparator.js: -------------------------------------------------------------------------------- 1 | // Merge sort comparator 2 | 3 | // Merge Sort function 4 | // Implement the merge sort algorithm. 5 | // Given an array, this algorithm will sort the values in the array. 6 | // The function take 2 parameters: an array and an optional comparator function. 7 | // The comparator function is a callback that will take two values from the array to be compared. 8 | // The function returns a negative value if the first value is less than the second, 9 | // a positive value if the first value is greater than the second, 10 | // and 0 if both values are equal. 11 | // The default comparator you provide should assume that the two parameters are numbers 12 | // and that we are sorting the values from smallest to largest. 13 | 14 | // Merge Helper function 15 | // Given two sorted arrays, write a function called merge which accepts two SORTED arrays 16 | // and returns a new array with both of the values from each array sorted. 17 | // This function should run in O(n + m) time and O(n + m) space 18 | // and should not modify the parameters passed to it. 19 | // The function should default to sorting numbers in ascending order. 20 | // If you pass in a comparator function as a third argument, 21 | // this comparator is what will be used. 22 | // (Note that the input arrays will always be sorted according to the comparator!) 23 | // Also, do not use the built in .sort method! We're going to use this helper 24 | // to implement a sort, so the helper itself shouldn't depend on a sort. 25 | 26 | function mergeSort(arr, comparator) { 27 | if (arr.length <= 1) return arr; 28 | 29 | const middle = Math.floor(arr.length / 2); 30 | const left = arr.slice(0, middle); 31 | const right = arr.slice(middle); 32 | 33 | return merge(mergeSort(left, comparator), mergeSort(right, comparator), comparator); 34 | } 35 | 36 | function merge(left, right, comparator) { 37 | if (typeof comparator !== 'function') { 38 | comparator = (a, b) => { 39 | if (a < b) return -1; 40 | if (a > b) return 1; 41 | return 0; 42 | }; 43 | } 44 | const resultArr = []; 45 | let leftCount = 0; 46 | let rightCount = 0; 47 | 48 | while (leftCount < left.length && rightCount < right.length) { 49 | if (comparator(left[leftCount], right[rightCount]) < 0) { 50 | resultArr.push(left[leftCount]); 51 | leftCount++; 52 | } else { 53 | resultArr.push(right[rightCount]); 54 | rightCount++; 55 | } 56 | } 57 | 58 | while (leftCount < left.length) { 59 | resultArr.push(left[leftCount]); 60 | leftCount++; 61 | } 62 | 63 | while (rightCount < right.length) { 64 | resultArr.push(right[rightCount]); 65 | rightCount++; 66 | } 67 | 68 | return resultArr; 69 | } 70 | 71 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 72 | console.log(mergeSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 73 | 74 | console.log(mergeSort(['LilBub', 'Garfield', 'Blue', 'Grumpy'], (a, b) => { 75 | if (a[1] < b[1]) return -1; 76 | if (a[1] > b[1]) return 1; 77 | return 0; 78 | })); // [ 'Garfield', 'LilBub', 'Blue', 'Grumpy' ] 79 | 80 | const moarKittyData = [{ 81 | name: 'LilBub', 82 | age: 7 83 | }, { 84 | name: 'Garfield', 85 | age: 40 86 | }, { 87 | name: 'Heathcliff', 88 | age: 45 89 | }, { 90 | name: 'Blue', 91 | age: 1 92 | }, { 93 | name: 'Grumpy', 94 | age: 6 95 | }]; 96 | 97 | console.log(mergeSort(moarKittyData, (a, b) => b.age - a.age)); // sorted by age in descending order 98 | // [ { name: 'Heathcliff', age: 45 }, 99 | // { name: 'Garfield', age: 40 }, 100 | // { name: 'LilBub', age: 7 }, 101 | // { name: 'Grumpy', age: 6 }, 102 | // { name: 'Blue', age: 1 } ] 103 | -------------------------------------------------------------------------------- /src/sorting-algorithms/merge-sort/merge-sort-modify-original-array.js: -------------------------------------------------------------------------------- 1 | function mergeSort(arr, start = 0, end = arr.length - 1) { 2 | if (start < end) { 3 | const center = Math.floor((start + end) / 2); 4 | mergeSort(arr, start, center); 5 | mergeSort(arr, center + 1, end); 6 | merge(arr, start, center, end); 7 | } 8 | } 9 | 10 | function merge(arr, start, center, end) { 11 | const left = arr.slice(start, center + 1); 12 | const right = arr.slice(center + 1, end + 1); 13 | let leftCount = 0; 14 | let rightCount = 0; 15 | let resultCount = start; 16 | 17 | while (leftCount < left.length && rightCount < right.length) { 18 | if (left[leftCount] < right[rightCount]) { 19 | arr[resultCount] = left[leftCount]; 20 | leftCount++; 21 | } else { 22 | arr[resultCount] = right[rightCount]; 23 | rightCount++; 24 | } 25 | 26 | resultCount++; 27 | } 28 | 29 | while (leftCount < left.length) { 30 | arr[resultCount] = left[leftCount]; 31 | leftCount++; 32 | resultCount++; 33 | } 34 | 35 | while (rightCount < right.length) { 36 | arr[resultCount] = right[rightCount]; 37 | rightCount++; 38 | resultCount++; 39 | } 40 | } 41 | 42 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 43 | mergeSort(nums); 44 | console.log(nums); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 45 | -------------------------------------------------------------------------------- /src/sorting-algorithms/merge-sort/merge-sort-modify-parameters.js: -------------------------------------------------------------------------------- 1 | // Merge sort (merge function modifies passed parameters) 2 | // Merge sort is an O(n * log(n)) algorithm. 3 | 4 | function mergeSort(arr) { 5 | if (arr.length <= 1) return arr; 6 | 7 | const center = Math.floor(arr.length / 2); 8 | const left = arr.slice(0, center); 9 | const right = arr.slice(center); 10 | 11 | return merge(mergeSort(left), mergeSort(right)); 12 | } 13 | 14 | function merge(left, right) { 15 | const helperArr = []; 16 | 17 | while (left.length && right.length) { 18 | if (left[0] < right[0]) helperArr.push(left.shift()); 19 | else helperArr.push(right.shift()); 20 | } 21 | 22 | return [...helperArr, ...left, ...right]; 23 | } 24 | 25 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 26 | console.log(mergeSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 27 | console.log(mergeSort([0, -10, 7, 4])); // [-10, 0, 4, 7] 28 | -------------------------------------------------------------------------------- /src/sorting-algorithms/merge-sort/merge-sort-not-modify-paramentrs.js: -------------------------------------------------------------------------------- 1 | // Merge sort (merge function does not modify passed parameters) 2 | // Merge sort is an O(n * log(n)) algorithm. 3 | 4 | function mergeSort(arr) { 5 | if (arr.length <= 1) return arr; 6 | 7 | const center = Math.floor(arr.length / 2); 8 | const left = arr.slice(0, center); 9 | const right = arr.slice(center); 10 | 11 | return merge(mergeSort(left), mergeSort(right)); 12 | } 13 | 14 | function merge(left, right) { 15 | const resultArr = []; 16 | let leftCount = 0; 17 | let rightCount = 0; 18 | 19 | while (leftCount < left.length && rightCount < right.length) { 20 | if (left[leftCount] < right[rightCount]) { 21 | resultArr.push(left[leftCount]); 22 | leftCount++; 23 | } else { 24 | resultArr.push(right[rightCount]); 25 | rightCount++; 26 | } 27 | } 28 | 29 | while (leftCount < left.length) { 30 | resultArr.push(left[leftCount]); 31 | leftCount++; 32 | } 33 | 34 | while (rightCount < right.length) { 35 | resultArr.push(right[rightCount]); 36 | rightCount++; 37 | } 38 | 39 | return resultArr; 40 | } 41 | 42 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 43 | console.log(mergeSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 44 | console.log(mergeSort([0, -10, 7, 4])); // [-10, 0, 4, 7] 45 | -------------------------------------------------------------------------------- /src/sorting-algorithms/quick-sort/quick-sort-comparator.js: -------------------------------------------------------------------------------- 1 | // Quick Sort comparator 2 | 3 | // Quick Sort function 4 | // Implement the quick sort algorithm. 5 | // Given an array, this algorithm will sort the values in the array. 6 | // The comparator function is a callback that will take two values from the array to be compared. 7 | // The function returns a negative value if the first value is less than the second, 8 | // a positive value if the first value is greater than the second, 9 | // and 0 if both values are equal. 10 | // The default comparator you provide should assume that the two parameters are numbers 11 | // and that we are sorting the values from smallest to largest. 12 | 13 | // Pivot Helper function 14 | // In this exercise, your goal is to implement a function called pivot. 15 | // This function contains nearly all of the logic you'll need in order to implement Quick Sort. 16 | // The pivot function is responsible for taking an array, setting the pivot value, 17 | // and mutating the array so that all values less than the pivot wind up to the left of it, 18 | // and all values greater than the pivot wind up to the right of it. 19 | // It's also helpful if this helper returns the index of where the pivot value winds up. 20 | // Hint: When we get to Quick Sort function, it will be helpful for the pivot helper 21 | // to accept not only an array and an optional comparator, 22 | // but also an optional start and end index. 23 | // These should default to 0 and the array length minus 1, respectively. 24 | 25 | function pivot(arr, comparator, start = 0, end = arr.length - 1) { 26 | if (typeof comparator !== 'function') { 27 | comparator = (a, b) => { 28 | if (a < b) return -1; 29 | if (a > b) return 1; 30 | return 0; 31 | }; 32 | } 33 | 34 | let pivotIndex = start; 35 | 36 | for (let i = start + 1; i <= end; i++) { 37 | if (comparator(arr[start], arr[i]) > 0) { 38 | pivotIndex++; 39 | [arr[i], arr[pivotIndex]] = [arr[pivotIndex], arr[i]]; 40 | } 41 | } 42 | 43 | if (pivotIndex !== start) [arr[pivotIndex], arr[start]] = [arr[start], arr[pivotIndex]]; 44 | 45 | return pivotIndex; 46 | } 47 | 48 | function quickSort(arr, comparator, start = 0, end = arr.length - 1) { 49 | if (start < end) { 50 | const pivotIndex = pivot(arr, comparator, start, end); 51 | 52 | quickSort(arr, comparator, start, pivotIndex - 1); 53 | quickSort(arr, comparator, pivotIndex + 1, end); 54 | } 55 | 56 | return arr; 57 | } 58 | 59 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 60 | console.log(quickSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 61 | console.log(quickSort([])); // [] 62 | 63 | console.log(quickSort(['LilBub', 'Garfield', 'Blue', 'Grumpy'], (a, b) => { 64 | if (a[1] < b[1]) return -1; 65 | if (a[1] > b[1]) return 1; 66 | return 0; 67 | })); // [ 'Garfield', 'LilBub', 'Blue', 'Grumpy' ] 68 | 69 | const moarKittyData = [{ 70 | name: 'LilBub', 71 | age: 7 72 | }, { 73 | name: 'Garfield', 74 | age: 40 75 | }, { 76 | name: 'Heathcliff', 77 | age: 45 78 | }, { 79 | name: 'Blue', 80 | age: 1 81 | }, { 82 | name: 'Grumpy', 83 | age: 6 84 | }]; 85 | 86 | console.log(quickSort(moarKittyData, (a, b) => b.age - a.age)); // sorted by age in descending order 87 | // [ { name: 'Heathcliff', age: 45 }, 88 | // { name: 'Garfield', age: 40 }, 89 | // { name: 'LilBub', age: 7 }, 90 | // { name: 'Grumpy', age: 6 }, 91 | // { name: 'Blue', age: 1 } ] 92 | -------------------------------------------------------------------------------- /src/sorting-algorithms/quick-sort/quick-sort.js: -------------------------------------------------------------------------------- 1 | // Quick sort 2 | // Quick sort is an O(n * log(n)) algorithm (Worst case - O(n^2). 3 | // Pivot is always the first element 4 | 5 | function pivot(arr, start = 0, end = arr.length - 1) { 6 | let pivotIndex = start; 7 | 8 | for (let i = start + 1; i <= end; i++) { 9 | if (arr[start] > arr[i]) { 10 | pivotIndex++; 11 | [arr[i], arr[pivotIndex]] = [arr[pivotIndex], arr[i]]; 12 | } 13 | } 14 | 15 | if (pivotIndex !== start) [arr[pivotIndex], arr[start]] = [arr[start], arr[pivotIndex]]; 16 | 17 | return pivotIndex; 18 | } 19 | 20 | function quickSort(arr, start = 0, end = arr.length - 1) { 21 | if (start < end) { 22 | const pivotIndex = pivot(arr, start, end); 23 | 24 | quickSort(arr, start, pivotIndex - 1); 25 | quickSort(arr, pivotIndex + 1, end); 26 | } 27 | 28 | return arr; 29 | } 30 | 31 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 32 | console.log(quickSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 33 | console.log(quickSort([])); // [] 34 | -------------------------------------------------------------------------------- /src/sorting-algorithms/radix-sort/radix-sort.js: -------------------------------------------------------------------------------- 1 | // Radix Sort 2 | 3 | // Radix Sort Helper function - getDigit 4 | // Implement a function called getDigit which accepts a positive integer and 5 | // a position, and returns the digit in that number at the given position. 6 | // The position reads from right to left, so the 0th position corresponds to the rightmost digit. 7 | 8 | // Radix Sort Helper - digitCount 9 | // Implement a function called digitCount which accepts a positive integer and 10 | // returns the number of digits that the integer has. 11 | 12 | // Radix Sort Helper - mostDigits 13 | // Implement a function called mostDigits which accepts an array of integers and 14 | // returns a count of the number of digits for the number in the array with the most digits. 15 | 16 | // Radix Sort function - radixSort 17 | // Write a function called radixSort which accepts an array of numbers and 18 | // sorts them in ascending order. 19 | 20 | function getDigit(num, i) { 21 | let result = Math.abs(num); 22 | 23 | for (let count = i; count > 0; count--) { 24 | result = Math.floor(result / 10); 25 | } 26 | 27 | return result % 10; 28 | } 29 | 30 | function digitCount(num) { 31 | return Math.abs(num).toString().length; 32 | } 33 | 34 | function mostDigits(nums) { 35 | let max = 0; 36 | 37 | for (const num of nums) { 38 | max = Math.max(max, digitCount(num)); 39 | } 40 | 41 | return max; 42 | } 43 | 44 | function radixSort(nums) { 45 | const end = mostDigits(nums); 46 | 47 | for (let i = 0; i < end; i++) { 48 | const helperArr = Array.from({ length: 10 }, () => []); 49 | 50 | for (const num of nums) { 51 | helperArr[getDigit(num, i)].push(num); 52 | } 53 | 54 | nums = [].concat(...helperArr); 55 | } 56 | 57 | return nums; 58 | } 59 | 60 | console.log(radixSort([8, 6, 1, 12])); // [1, 6, 8, 12] 61 | console.log(radixSort([10, 100, 1, 1000, 10000000])); // [1, 10, 100, 1000, 10000000] 62 | console.log(radixSort([902, 4, 7, 408, 29, 9637, 1556, 3556, 8157, 4386, 86, 593])); 63 | // [4, 7, 29, 86, 408, 593, 902, 1556, 3556, 4386, 8157, 9637] 64 | -------------------------------------------------------------------------------- /src/sorting-algorithms/selection-sort/selection-sort-comparator.js: -------------------------------------------------------------------------------- 1 | // Selection Sort Comparator 2 | 3 | // Implement a function called selectionSort. 4 | // Given an array, selectionSort will sort the values in the array. 5 | // The function takes 2 parameters: an array and an optional comparator function. 6 | // The comparator function is a callback that will take two values from the array to be compared. 7 | // The function returns a negative value if the first value is less than the second, 8 | // a positive value if the first value is greater than the second, 9 | // and 0 if both values are equal. 10 | 11 | // The default comparator you provide should assume that the two parameters are 12 | // numbers and that we are sorting the values from smallest to largest. 13 | 14 | function selectionSort(arr, comparator) { 15 | if (typeof comparator !== 'function') { 16 | comparator = (a, b) => { 17 | if (a < b) return -1; 18 | if (a > b) return 1; 19 | return 0; 20 | }; 21 | } 22 | 23 | let min; 24 | 25 | for (let i = 0; i < arr.length; i++) { 26 | min = i; 27 | 28 | for (let j = i + 1; j < arr.length; j++) { 29 | if (comparator(arr[min], arr[j]) > 0) min = j; 30 | } 31 | 32 | if (min !== i) [arr[i], arr[min]] = [arr[min], arr[i]]; 33 | } 34 | 35 | return arr; 36 | } 37 | 38 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 39 | console.log(selectionSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 40 | 41 | console.log(selectionSort(['LilBub', 'Garfield', 'Blue', 'Grumpy'], (a, b) => { 42 | if (a[1] < b[1]) return -1; 43 | if (a[1] > b[1]) return 1; 44 | return 0; 45 | })); // [ 'Garfield', 'LilBub', 'Blue', 'Grumpy' ] 46 | 47 | const moarKittyData = [{ 48 | name: 'LilBub', 49 | age: 7 50 | }, { 51 | name: 'Garfield', 52 | age: 40 53 | }, { 54 | name: 'Heathcliff', 55 | age: 45 56 | }, { 57 | name: 'Blue', 58 | age: 1 59 | }, { 60 | name: 'Grumpy', 61 | age: 6 62 | }]; 63 | 64 | console.log(selectionSort(moarKittyData, (a, b) => b.age - a.age)); // sorted by age in descending order 65 | // [ { name: 'Heathcliff', age: 45 }, 66 | // { name: 'Garfield', age: 40 }, 67 | // { name: 'LilBub', age: 7 }, 68 | // { name: 'Grumpy', age: 6 }, 69 | // { name: 'Blue', age: 1 } ] 70 | -------------------------------------------------------------------------------- /src/sorting-algorithms/selection-sort/selection-sort.js: -------------------------------------------------------------------------------- 1 | // Selection Sort 2 | // Selection sort is an O(n^2) algorithm. 3 | 4 | function selectionSort(arr) { 5 | let min; 6 | 7 | for (let i = 0; i < arr.length; i++) { 8 | min = i; 9 | 10 | for (let j = i + 1; j < arr.length; j++) { 11 | if (arr[j] < arr[min]) min = j; 12 | } 13 | if (min !== i) [arr[i], arr[min]] = [arr[min], arr[i]]; 14 | } 15 | 16 | return arr; 17 | } 18 | 19 | const nums = [4, 3, 5, 3, 43, 232, 4, 34, 232, 32, 4, 35, 34, 23, 2, 453, 546, 75, 67, 4342, 32]; 20 | console.log(selectionSort(nums)); // [2, 3, 3, 4, 4, 4, 5, 23, 32, 32, 34, 34, 35, 43, 67, 75, 232, 232, 453, 546, 4342] 21 | console.log(selectionSort([0, -10, 7, 4])); // [-10, 0, 4, 7] 22 | --------------------------------------------------------------------------------