├── .gitignore ├── src ├── 10-tree │ ├── compare.js │ ├── leetcode │ │ ├── maximum-depth-of-binary-tree.ts │ │ ├── minimum-depth-of-binary-tree.ts │ │ ├── convert-sorted-array-to-binary-search-tree.ts │ │ ├── validate-binary-search-tree.ts │ │ ├── binary-tree-preorder-traversal.ts │ │ ├── binary-tree-inorder-traversal.ts │ │ └── binary-tree-postorder-traversal.ts │ ├── comparator.js │ ├── fenwick-tree.js │ ├── 01-bst.js │ ├── 05-avl.js │ ├── 06-red-black.js │ ├── 04-post-order-traversal.js │ ├── 02-in-order-traversal.js │ ├── 03-pre-order-traversal.js │ └── segment-tree.js ├── 09-recursion │ ├── 03-callstack.js │ ├── leetcode │ │ ├── fibonacci-number.ts │ │ └── power-of-two.ts │ ├── 02-factorial.js │ ├── 01-intro-recursion.js │ └── 04-fibonacci.js ├── 02-bigOnotation │ ├── 02-bigOChart.html │ ├── 02-bigOChart.js │ ├── 03-exercises.js │ └── 01-big-o-intro.js ├── 03-array │ ├── 08-TypedArrays.js │ ├── 08-TypedArrays.ts │ ├── hackerrank │ │ ├── 02-array-left-rotation.js │ │ ├── 02-array-left-rotation.ts │ │ ├── 01-arrays-ds.js │ │ └── 01-arrays-ds.ts │ ├── 09-arrays-typescript.js │ ├── 09-arrays-typescript.ts │ ├── 08-different-data-types.js │ ├── 03-iterator-functions.ts │ ├── 05-transforming-array.ts │ ├── 10-todo-list-example.html │ ├── 02-adding-removing-elements.ts │ ├── 01-arrays.ts │ ├── 01-arrays.js │ ├── 06-other-methods.ts │ ├── 05-transforming-array.js │ ├── 03-iterator-functions.js │ ├── 11-array-chunking.js │ ├── 07-multidimensional-arrays.ts │ ├── 06-other-methods.js │ ├── 07-multidimensional-arrays.js │ └── 11-array-chunking.ts ├── 08-dictionary-hash │ ├── 03-using-weakmap-class.js │ ├── leetcode │ │ ├── two-sum.ts │ │ └── integer-to-roman.ts │ ├── 04-using-hashmap-class.js │ ├── dictionary.js │ ├── 02-using-map-class.js │ ├── 01-using-dictionary-class.js │ ├── hash-table.ts │ ├── hash-table.js │ ├── 05-hashmap-collision.js │ ├── hash-table-separate-chaining.js │ └── hash-table-linear-probing.js ├── 13-graph │ ├── 06-using-prim.js │ ├── 07-using-kruskal.js │ ├── 01-airline-system.js │ ├── floyd-warshall.js │ ├── graph.js │ ├── leetcode │ │ ├── number-of-islands.ts │ │ └── course-schedule.ts │ ├── prim.js │ ├── dijkstra.js │ ├── 04-using-dijkstra.js │ ├── 05-using-floyd-warshall.js │ ├── kruskal.js │ ├── bfs.js │ ├── 03-using-dfs.js │ ├── 02-using-bfs.js │ └── dfs.js ├── 01-intro │ ├── 04-functions.js │ ├── 03-loops.js │ ├── 05-scope.js │ ├── 02-conditionals.js │ ├── 08-typescript.js │ ├── 07-modern.js │ ├── 08-typescript.ts │ ├── 01-hello-variables.js │ └── 06-objects.js ├── 05-queue-deque │ ├── palindrome-checker.js │ ├── 01-using-queue-class.js │ ├── leetcode │ │ ├── time-needed-to-buy-tickets.ts │ │ ├── ex.md │ │ └── number-of-students-unable-to-eat-lunch.ts │ ├── queue.js │ ├── 02-using-queue-two-pointers.js │ ├── queue.ts │ ├── deque.js │ ├── 03-using-deque-class.js │ ├── deque.ts │ ├── hot-potato.js │ └── __test__ │ │ ├── queue.test.ts │ │ └── deque.test.ts ├── 04-stack │ ├── decimal-to-binary.js │ ├── decimal-to-binary.ts │ ├── tower-of-hanoi.js │ ├── __test__ │ │ ├── decimal-to-base.test.ts │ │ ├── decimal-to-binary.test.ts │ │ └── stack.test.ts │ ├── stack.ts │ ├── 02-base-converter-examples.ts │ ├── 02-base-converter-examples.js │ ├── 01-undo-feature.js │ ├── 01-using-stack-class.ts │ ├── leetcode │ │ ├── simplify-path.ts │ │ ├── min-stack.ts │ │ └── valid-parentheses.ts │ ├── stack.js │ ├── decimal-to-base.ts │ └── decimal-to-base.js ├── 06-linked-list │ └── leetcode │ │ └── reverse-linked-list.ts ├── 11-heap │ ├── 03-heap-sort.js │ ├── 02-max-heap.js │ ├── 01-min-heap.js │ ├── leetcode │ │ ├── minimum-number-game.ts │ │ └── relative-ranks.ts │ ├── heap-sort.js │ └── heap.js ├── 07-set │ ├── leetcode │ │ └── remove-duplicates-from-sorted-array.ts │ ├── 02-using-set-class.js │ └── set.js └── 12-trie │ ├── 01-spell-checker.js │ ├── leetcode │ ├── implement-trie-prefix-tree.ts │ ├── design-add-and-search-words-data-structure.ts │ └── longest-common-prefix.ts │ ├── trie.js │ └── trie.ts ├── .editorconfig ├── tsconfig.json ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ -------------------------------------------------------------------------------- /src/10-tree/compare.js: -------------------------------------------------------------------------------- 1 | const Compare = { 2 | LESS_THAN: -1, 3 | BIGGER_THAN: 1, 4 | EQUALS: 0 5 | }; 6 | 7 | module.exports = Compare; -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = LF 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /src/09-recursion/03-callstack.js: -------------------------------------------------------------------------------- 1 | // src/09-recursion/03-callstack.js 2 | 3 | // stack overflow 4 | let count = 0; 5 | function recursiveFn() { 6 | count++; 7 | recursiveFn(); 8 | } 9 | 10 | try { 11 | recursiveFn(); 12 | } catch (ex) { 13 | console.log('count = ' + count + ' error: ' + ex); 14 | } 15 | 16 | 17 | // to see the output of this file use the command: node src/09-recursion/03-callstack.js -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "CommonJS", 5 | "lib": ["ESNext"], 6 | //"outDir": "dist", 7 | "rootDir": "src", 8 | // "strict": true, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | }, 12 | "include": ["src/**/*"], 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } -------------------------------------------------------------------------------- /src/02-bigOnotation/02-bigOChart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | -------------------------------------------------------------------------------- /src/03-array/08-TypedArrays.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/08-TypedArrays.js 2 | 3 | // TypedArray example 4 | const arrayLength = 5; 5 | const int16 = new Int16Array(arrayLength); 6 | 7 | for (let i = 0; i < arrayLength; i++) { 8 | int16[i] = i + 1; 9 | } 10 | 11 | console.log(int16); 12 | 13 | // check https://js.tensorflow.org for more practical examples 14 | 15 | // to see the output of this file use the command: node src/03-array/08-TypedArrays.js -------------------------------------------------------------------------------- /src/03-array/08-TypedArrays.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/08-TypedArrays.ts 2 | 3 | // TypedArray example 4 | const arrayLength = 5; 5 | const int16 = new Int16Array(arrayLength); 6 | 7 | for (let i = 0; i < arrayLength; i++) { 8 | int16[i] = i + 1; 9 | } 10 | 11 | console.log(int16); 12 | 13 | // check https://js.tensorflow.org for more practical examples 14 | 15 | // to see the output of this file use the command: node src/03-array/08-TypedArrays.ts -------------------------------------------------------------------------------- /src/09-recursion/leetcode/fibonacci-number.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/fibonacci-number/description/ 2 | 3 | function fib(n: number): number { 4 | if (n < 2) { return n; } 5 | 6 | let prevPrev = 0; 7 | let prev = 1; 8 | let current; 9 | 10 | for (let i = 2; i <= n; i++) { // n >= 2 11 | current = prev + prevPrev; // f(n-1) + f(n-2) 12 | prevPrev = prev; 13 | prev = current; 14 | } 15 | 16 | return current; 17 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-datastructures-algorithms", 3 | "version": "4.0.0", 4 | "description": "Learning JavaScript Data Structures and Algorithms", 5 | "scripts": { 6 | "build:ts": "tsc -p tsconfig.json", 7 | "test": "jest" 8 | }, 9 | "author": "Loiane Groner", 10 | "devDependencies": { 11 | "@jest/globals": "^30.1.2", 12 | "jest": "^30.1.3", 13 | "ts-jest": "^29.4.1", 14 | "ts-node": "^10.9.2", 15 | "typescript": "^5.9.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/08-dictionary-hash/03-using-weakmap-class.js: -------------------------------------------------------------------------------- 1 | const privateData = new WeakMap(); 2 | 3 | class Person { 4 | constructor(name, age) { 5 | this.name = name; 6 | this.age = age; 7 | privateData.set(this, { ssn: 'XXX-XX-XXXX', medicalHistory: [] }); 8 | } 9 | 10 | getSSN() { 11 | return privateData.get(this)?.ssn; 12 | } 13 | } 14 | 15 | const alice = new Person("Penelope", 20); 16 | console.log(alice.name); // Penelope 17 | console.log(alice.getSSN()); // XXX-XX-XXXX 18 | 19 | // Try to access private data -------------------------------------------------------------------------------- /src/03-array/hackerrank/02-array-left-rotation.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/hackerrank/02-array-left-rotation.js 2 | // Problem: https://www.hackerrank.com/challenges/array-left-rotation/problem 3 | // Solution 1 4 | function rotLeft(a, d) { 5 | return a.concat(a.splice(0, d)); 6 | } 7 | // Solution 2 8 | function rotLeft2(a, d) { 9 | return [...a.slice(d), ...a.slice(0, d)]; 10 | } 11 | // Test cases 12 | console.log(rotLeft([1, 2, 3, 4, 5], 4)); // [5, 1, 2, 3, 4] 13 | console.log(rotLeft2([1, 2, 3, 4, 5], 4)); // [5, 1, 2, 3, 4] 14 | -------------------------------------------------------------------------------- /src/13-graph/06-using-prim.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/06-using-prim.js 2 | 3 | const prim = require('./prim'); 4 | 5 | const cityNames = ['Erebor', 'Rivendell', 'Hobbiton', 'Isengard', 'Rohan', 'Lothlorien']; 6 | 7 | const cities = [ 8 | [0, 2, 4, 0, 0, 0], 9 | [2, 0, 2, 4, 2, 0], 10 | [4, 2, 0, 0, 3, 0], 11 | [0, 4, 0, 0, 3, 2], 12 | [0, 2, 3, 3, 0, 2], 13 | [0, 0, 0, 2, 2, 0] 14 | ]; 15 | 16 | const mst = prim(cities); 17 | console.log('Minimum Spanning Tree:', mst); 18 | 19 | // [ -1, 0, 1, 5, 1, 4 ] 20 | 21 | // to see the output of this file use the command: node src/13-graph/06-using-prim.js -------------------------------------------------------------------------------- /src/03-array/09-arrays-typescript.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/09-arrays-typescript.js 2 | // @ts-ignore 3 | const friends = [ 4 | { name: 'Frodo', age: 30 }, 5 | { name: 'Violet', age: 18 }, 6 | { name: 'Aelin', age: 20 } 7 | ]; 8 | // @ts-ignore 9 | const compareFriends = (friendA, friendB) => { 10 | return friendA.age - friendB.age; 11 | }; 12 | friends.sort(compareFriends); 13 | console.log('Sorted friends:', friends); // [ { name: 'Violet', age: 18 }, { name: 'Aelin', age: 20 }, { name: 'Frodo', age: 30 } ] 14 | // to see the output of this file use the command: node src/03-array/09-arrays-typescript.js 15 | -------------------------------------------------------------------------------- /src/13-graph/07-using-kruskal.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/07-using-kruskal.js 2 | 3 | const kruskal = require('./kruskal'); 4 | 5 | const cityNames = ['Erebor', 'Rivendell', 'Hobbiton', 'Isengard', 'Rohan', 'Lothlorien']; 6 | 7 | const cities = [ 8 | [0, 2, 4, 0, 0, 0], 9 | [2, 0, 2, 4, 2, 0], 10 | [4, 2, 0, 0, 3, 0], 11 | [0, 4, 0, 0, 3, 2], 12 | [0, 2, 3, 3, 0, 2], 13 | [0, 0, 0, 2, 2, 0] 14 | ]; 15 | 16 | const mst = kruskal(cities); 17 | 18 | console.log('Minimum Spanning Tree with Kruskal:', mst); 19 | 20 | // [ [ 0, 1 ], [ 1, 2 ], [ 1, 4 ], [ 3, 5 ], [ 4, 5 ] ] 21 | 22 | // to see the output of this file use the command: node src/13-graph/07-using-kruskal.js -------------------------------------------------------------------------------- /src/10-tree/leetcode/maximum-depth-of-binary-tree.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/maximum-depth-of-binary-tree/description/ 2 | // 104. Maximum Depth of Binary Tree 3 | 4 | class TreeNode { 5 | val: number 6 | left: TreeNode | null 7 | right: TreeNode | null 8 | constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { 9 | this.val = (val === undefined ? 0 : val) 10 | this.left = (left === undefined ? null : left) 11 | this.right = (right === undefined ? null : right) 12 | } 13 | } 14 | 15 | function maxDepth(root: TreeNode | null): number { 16 | if (!root) return 0; 17 | return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); 18 | } -------------------------------------------------------------------------------- /src/01-intro/04-functions.js: -------------------------------------------------------------------------------- 1 | // Path: src/01-intro/04-functions.js 2 | 3 | function sayHello(name) { 4 | console.log('Hello! ', name); 5 | } 6 | 7 | sayHello('Packt'); // Hello! Packt 8 | 9 | /* function using the return statement */ 10 | function sum(num1, num2) { 11 | return num1 + num2; 12 | } 13 | 14 | const result = sum(1, 2); 15 | console.log(result); // outputs 3 16 | 17 | /* function with default parameter */ 18 | function sumDefault(num1, num2 = 2) { // num2 has a default value 19 | return num1 + num2; 20 | } 21 | console.log(sumDefault(1)); // outputs 3 22 | console.log(sumDefault(1, 3)); // outputs 4 23 | 24 | // to see the output of this file use the command: node src/01-intro/04-functions.js -------------------------------------------------------------------------------- /src/03-array/09-arrays-typescript.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/09-arrays-typescript.ts 2 | 3 | interface Friend { 4 | name: string; 5 | age: number; 6 | } 7 | 8 | // @ts-ignore 9 | const friends = [ 10 | { name: 'Frodo', age: 30 }, 11 | { name: 'Violet', age: 18 }, 12 | { name: 'Aelin', age: 20 } 13 | ]; 14 | 15 | // @ts-ignore 16 | const compareFriends = (friendA: Friend, friendB: Friend) => { 17 | return friendA.age - friendB.age; 18 | }; 19 | friends.sort(compareFriends); 20 | console.log('Sorted friends:', friends); // [ { name: 'Violet', age: 18 }, { name: 'Aelin', age: 20 }, { name: 'Frodo', age: 30 } ] 21 | 22 | 23 | // to see the output of this file use the command: node src/03-array/09-arrays-typescript.ts -------------------------------------------------------------------------------- /src/05-queue-deque/palindrome-checker.js: -------------------------------------------------------------------------------- 1 | const Deque = require('./deque'); 2 | 3 | function isPalindrome(word) { 4 | 5 | if (word === undefined || word === null || (typeof word === 'string' && word.length === 0)) { 6 | return false; 7 | } 8 | 9 | const deque = new Deque(); 10 | word = word.toLowerCase().replace(/\s/g, ''); 11 | 12 | for (let i = 0; i < word.length; i++) { 13 | deque.addRear(word[i]); 14 | } 15 | 16 | // Check if the word is a palindrome 17 | while (deque.size() > 1) { 18 | if (deque.removeFront() !== deque.removeRear()) { 19 | return false; 20 | } 21 | } 22 | 23 | return true; 24 | } 25 | 26 | // Test the palindrome checker 27 | console.log(isPalindrome("racecar")); // Output: true -------------------------------------------------------------------------------- /src/10-tree/comparator.js: -------------------------------------------------------------------------------- 1 | // src/10-tree/comparator.js 2 | 3 | const Compare = require('./compare'); 4 | 5 | class Comparator { 6 | #compareFn; 7 | 8 | constructor(compareFn = Comparator.defaultCompareFn) { 9 | this.#compareFn = compareFn; 10 | } 11 | 12 | static defaultCompareFn(a, b) { 13 | if (a === b) { 14 | return Compare.EQUALS; 15 | } 16 | return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; 17 | } 18 | 19 | equal(a, b) { 20 | return this.#compareFn(a, b) === Compare.EQUALS; 21 | } 22 | 23 | lessThan(a, b) { 24 | return this.#compareFn(a, b) < Compare.EQUALS; 25 | } 26 | 27 | greaterThan(a, b) { 28 | return this.#compareFn(a, b) > Compare.EQUALS; 29 | } 30 | 31 | } 32 | 33 | module.exports = Comparator; -------------------------------------------------------------------------------- /src/09-recursion/02-factorial.js: -------------------------------------------------------------------------------- 1 | // src/09-recursion/02-factorial.js 2 | 3 | // iterative approach 4 | function factorialIterative(number) { 5 | if (number < 0) { 6 | return undefined; 7 | } 8 | let total = 1; 9 | for (let n = number; n > 1; n--) { 10 | total *= n; 11 | } 12 | return total; 13 | } 14 | 15 | console.log('5! =',factorialIterative(5)); // 5! = 120 16 | 17 | // recursive approach 18 | function factorial(number) { 19 | // console.trace(); 20 | if (number < 0) { return undefined; } 21 | if (number === 1 || number === 0) { // base case 22 | return 1; 23 | } 24 | return number * factorial(number - 1); 25 | } 26 | 27 | console.log('Recursive 5! =',factorial(5)); // Recursive 5! = 120 28 | 29 | // to see the output of this file use the command: node src/09-recursion/02-factorial.js -------------------------------------------------------------------------------- /src/05-queue-deque/01-using-queue-class.js: -------------------------------------------------------------------------------- 1 | // src/05-queue-deque/01-using-queue-class.js 2 | 3 | const Queue = require('./queue'); 4 | 5 | const queue = new Queue(); 6 | 7 | console.log(queue.isEmpty()); // true 8 | 9 | queue.enqueue({ document: 'Chapter05.docx', pages: 20 }); 10 | queue.enqueue({ document: 'JavaScript.pdf', pages: 60 }); 11 | queue.enqueue({ document: 'TypeScript.pdf', pages: 80 }); 12 | 13 | console.log(queue.toString()); 14 | 15 | console.log(queue.size); // 3 16 | 17 | console.log(queue.isEmpty()); // false 18 | 19 | console.log(queue.front()); // { document: 'Chapter05.docx', pages: 20 } 20 | 21 | // print all documents 22 | while (!queue.isEmpty()) { 23 | console.log(queue.dequeue()); 24 | } 25 | 26 | // to see the output of this file use the command: node src/05-queue-deque/01-using-queue-class.js -------------------------------------------------------------------------------- /src/09-recursion/01-intro-recursion.js: -------------------------------------------------------------------------------- 1 | // src/09-recursion/01-intro-recursion.js 2 | 3 | // To understand recursion, one must first understand recursion 4 | const readline = require('readline').createInterface({ 5 | input: process.stdin, 6 | output: process.stdout 7 | }); 8 | 9 | function understandRecursion() { 10 | readline.question('Do you understand recursion? (y/n) ', (answer) => { 11 | if (answer.toLowerCase() === 'y') { // Base case 12 | console.log("Excellent! You've grasped recursion."); 13 | readline.close(); // Exit the program 14 | } else { 15 | console.log("Let's try again..."); 16 | understandRecursion(); // Recursive call 17 | } 18 | }); 19 | } 20 | 21 | understandRecursion(); 22 | 23 | // to see the output of this file use the command: node src/09-recursion/01-intro-recursion.js 24 | -------------------------------------------------------------------------------- /src/10-tree/leetcode/minimum-depth-of-binary-tree.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/minimum-depth-of-binary-tree/description/ 2 | // 111. Minimum Depth of Binary Tree 3 | 4 | class TreeNode { 5 | val: number 6 | left: TreeNode | null 7 | right: TreeNode | null 8 | constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { 9 | this.val = (val === undefined ? 0 : val) 10 | this.left = (left === undefined ? null : left) 11 | this.right = (right === undefined ? null : right) 12 | } 13 | } 14 | 15 | function minDepth(root: TreeNode | null): number { 16 | if (!root) return 0; 17 | if (!root.left && !root.right) return 1; 18 | if (!root.left) return 1 + minDepth(root.right); 19 | if (!root.right) return 1 + minDepth(root.left); 20 | return 1 + Math.min(minDepth(root.left), minDepth(root.right)); 21 | } -------------------------------------------------------------------------------- /src/13-graph/01-airline-system.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/01-airline-system.js 2 | 3 | // import the Graph class 4 | const Graph = require('./graph'); 5 | 6 | const airline = new Graph(); 7 | 8 | const airports = 'MCO TPA JFK LAX SFO'.split(' '); 9 | 10 | airports.forEach(airport => 11 | airline.addVertex(airport) 12 | ); 13 | 14 | airline.addEdge('MCO', 'JFK'); 15 | airline.addEdge('MCO', 'LAX'); 16 | airline.addEdge('TPA', 'JFK'); 17 | airline.addEdge('TPA', 'LAX'); 18 | airline.addEdge('JFK', 'LAX'); 19 | airline.addEdge('JFK', 'SFO'); 20 | airline.addEdge('LAX', 'SFO'); 21 | 22 | console.log(airline.toString()); 23 | 24 | // Output: 25 | // MCO -> JFK LAX 26 | // TPA -> JFK LAX 27 | // JFK -> MCO TPA LAX SFO 28 | // LAX -> MCO TPA JFK SFO 29 | // SFO -> JFK LAX 30 | 31 | // to see the output of this file use the command: node src/13-graph/01-airline-system.js -------------------------------------------------------------------------------- /src/04-stack/decimal-to-binary.js: -------------------------------------------------------------------------------- 1 | // src/04-stack/decimal-to-binary.js 2 | 3 | const Stack = require('./stack'); 4 | 5 | /** 6 | * Converts a decimal number to binary 7 | * @param {number} decimalNumber - decimal number to be converted 8 | * @returns {string} binary representation of the decimal number 9 | */ 10 | function decimalToBinary(decimalNumber) { 11 | const remainderStack = new Stack(); 12 | let binaryString = ''; 13 | 14 | if (decimalNumber === 0) { 15 | return '0'; 16 | } 17 | 18 | while (decimalNumber > 0) { 19 | const remainder = Math.floor(decimalNumber % 2); 20 | remainderStack.push(remainder); 21 | decimalNumber = Math.floor(decimalNumber / 2); 22 | } 23 | 24 | while (!remainderStack.isEmpty()) { 25 | binaryString += remainderStack.pop().toString(); 26 | } 27 | 28 | return binaryString; 29 | } 30 | 31 | module.exports = decimalToBinary; -------------------------------------------------------------------------------- /src/10-tree/leetcode/convert-sorted-array-to-binary-search-tree.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/description/ 2 | // 108. Convert Sorted Array to Binary Search Tree 3 | 4 | class TreeNode { 5 | val: number 6 | left: TreeNode | null 7 | right: TreeNode | null 8 | constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { 9 | this.val = (val === undefined ? 0 : val) 10 | this.left = (left === undefined ? null : left) 11 | this.right = (right === undefined ? null : right) 12 | } 13 | } 14 | 15 | function sortedArrayToBST(nums: number[]): TreeNode | null { 16 | if (nums.length === 0) return null; 17 | const mid = Math.floor(nums.length / 2); 18 | const root = new TreeNode(nums[mid]); 19 | root.left = sortedArrayToBST(nums.slice(0, mid)); 20 | root.right = sortedArrayToBST(nums.slice(mid + 1)); 21 | return root; 22 | } -------------------------------------------------------------------------------- /src/03-array/hackerrank/02-array-left-rotation.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/hackerrank/02-array-left-rotation.ts 2 | // Problem: https://www.hackerrank.com/challenges/array-left-rotation/problem 3 | 4 | // Solution 1 5 | function rotLeft(a: number[], d: number): number[] { 6 | return a.concat(a.splice(0, d)); 7 | } 8 | 9 | // Solution 2 10 | function rotLeft2(a: number[], d: number): number[] { 11 | return [...a.slice(d), ...a.slice(0, d)]; 12 | } 13 | 14 | // Solution 3, swap the elements d times 15 | function rotLeft3(a: number[], d: number): number[] { 16 | for (let i = 0; i < d; i++) { 17 | const temp = a[0]; 18 | for (let j = 0; j < a.length - 1; j++) { 19 | a[j] = a[j + 1]; 20 | } 21 | a[a.length - 1] = temp; 22 | } 23 | return a; 24 | } 25 | 26 | 27 | // Test cases 28 | console.log(rotLeft([1, 2, 3, 4, 5], 4)); // [5, 1, 2, 3, 4] 29 | console.log(rotLeft2([1, 2, 3, 4, 5], 4)); // [5, 1, 2, 3, 4] 30 | -------------------------------------------------------------------------------- /src/04-stack/decimal-to-binary.ts: -------------------------------------------------------------------------------- 1 | // src/04-stack/decimal-to-binary.js 2 | 3 | import Stack from './stack'; 4 | 5 | /** 6 | * Converts a decimal number to binary 7 | * @param {number} decimalNumber - decimal number to be converted 8 | * @returns {string} binary representation of the decimal number 9 | */ 10 | function decimalToBinary(decimalNumber: number) { 11 | const remainderStack = new Stack(); 12 | let binaryString = ''; 13 | 14 | if (decimalNumber === 0) { 15 | return '0'; 16 | } 17 | 18 | while (decimalNumber > 0) { // {1} 19 | const remainder = Math.floor(decimalNumber % 2); // {2} 20 | remainderStack.push(remainder); // {3} 21 | decimalNumber = Math.floor(decimalNumber / 2); // {4} 22 | } 23 | 24 | while (!remainderStack.isEmpty()) { // {5} 25 | binaryString += remainderStack.pop().toString(); 26 | } 27 | 28 | return binaryString; 29 | } 30 | 31 | export default decimalToBinary; -------------------------------------------------------------------------------- /src/08-dictionary-hash/leetcode/two-sum.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/two-sum/ 2 | 3 | // Time complexity: O(n) 4 | // Space complexity: O(n) 5 | function twoSum(nums: number[], target: number): number[] { 6 | const map = new Map(); 7 | for (let i = 0; i < nums.length; i++) { 8 | const diff = target - nums[i]; 9 | if (map.has(diff)) { 10 | return [map.get(diff)!, i]; 11 | } 12 | map.set(nums[i], i); 13 | } 14 | return []; 15 | } 16 | 17 | // Test cases 18 | console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1] 19 | console.log(twoSum([3, 2, 4], 6)); // [1, 2] 20 | console.log(twoSum([3, 3], 6)); // [0, 1] 21 | 22 | function twoSum2(nums: number[], target: number): number[] { 23 | for (let i=0; i [t, i]); 10 | let time = 0; 11 | while(queue.length > 0) { 12 | let [t, i] = queue.shift(); 13 | if(t - k <= 0) { 14 | time += t; 15 | break; 16 | } else { 17 | time += k; 18 | queue.push([t - k, i]); 19 | } 20 | } 21 | 22 | return time; 23 | }; 24 | 25 | // Time complexity: O(n) 26 | // Space complexity: O(n) 27 | 28 | console.log(timeRequiredToBuy([2,6,3,4,5], 2)); // 14 29 | console.log(timeRequiredToBuy([2,3,2], 2)); // 6 30 | console.log(timeRequiredToBuy([5,1,1,1], 0)); // 11 31 | -------------------------------------------------------------------------------- /src/08-dictionary-hash/leetcode/integer-to-roman.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/integer-to-roman/description/ 2 | 3 | // Time complexity: O(1) 4 | function intToRoman(num: number): string { 5 | // define map 6 | const romanMap = { 7 | M:1000, 8 | CM:900, 9 | D:500, 10 | CD:400, 11 | C:100, 12 | XC:90, 13 | L:50, 14 | XL:40, 15 | X:10, 16 | IX:9, 17 | V:5, 18 | IV:4, 19 | I:1 20 | }; 21 | 22 | let result = ''; 23 | for(let romanNum in romanMap){ 24 | while (num >= romanMap[romanNum]) { 25 | result += romanNum; 26 | num -= romanMap[romanNum]; 27 | } 28 | } 29 | return result; 30 | }; 31 | 32 | // Test cases 33 | console.log(intToRoman(3)); // "III" 34 | console.log(intToRoman(4)); // "IV" 35 | console.log(intToRoman(9)); // "IX" 36 | console.log(intToRoman(58)); // "LVIII" 37 | console.log(intToRoman(1994)); // "MCMXCIV" 38 | console.log(intToRoman(3999)); // "MMMCM" 39 | -------------------------------------------------------------------------------- /src/06-linked-list/leetcode/reverse-linked-list.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Definition for singly-linked list. 4 | * class ListNode { 5 | * val: number 6 | * next: ListNode | null 7 | * constructor(val?: number, next?: ListNode | null) { 8 | * this.val = (val===undefined ? 0 : val) 9 | * this.next = (next===undefined ? null : next) 10 | * } 11 | * } 12 | */ 13 | 14 | class ListNode { 15 | val: number 16 | next: ListNode | null 17 | constructor(val?: number, next?: ListNode | null) { 18 | this.val = (val === undefined ? 0 : val) 19 | this.next = (next === undefined ? null : next) 20 | } 21 | } 22 | 23 | function reverseList(head: ListNode | null): ListNode | null { 24 | let current = head; 25 | let newHead = null; 26 | let nextNode = null; 27 | while (current) { 28 | nextNode = current.next; 29 | current.next = newHead; 30 | newHead = current; 31 | current = nextNode; 32 | } 33 | return newHead; 34 | } -------------------------------------------------------------------------------- /src/04-stack/tower-of-hanoi.js: -------------------------------------------------------------------------------- 1 | // src/04-stack/tower-of-hanoi.js 2 | 3 | const Stack = require('./stack'); 4 | 5 | /** 6 | * Solves the Tower of Hanoi puzzle 7 | * @param {number} n - number of disks 8 | * @param {Stack} source - source stack 9 | * @param {Stack} auxiliary - auxiliary stack 10 | * @param {Stack} destination - destination stack 11 | */ 12 | function towerOfHanoi(n, source, auxiliary, destination) { 13 | if (n > 0) { 14 | towerOfHanoi(n - 1, source, destination, auxiliary); 15 | destination.push(source.pop()); 16 | towerOfHanoi(n - 1, auxiliary, source, destination); 17 | } 18 | } 19 | 20 | 21 | // Usage 22 | // const source = new Stack(); 23 | // const auxiliary = new Stack(); 24 | // const destination = new Stack(); 25 | // source.push(3); 26 | // source.push(2); 27 | // source.push(1); 28 | // towerOfHanoi(3, source, auxiliary, destination); 29 | // console.log(destination); // [1, 2, 3] 30 | 31 | 32 | module.exports = towerOfHanoi; 33 | -------------------------------------------------------------------------------- /src/05-queue-deque/queue.js: -------------------------------------------------------------------------------- 1 | // src/05-queue-deque/queue.js 2 | 3 | class Queue { 4 | 5 | #items = []; 6 | 7 | enqueue(item) { 8 | this.#items.push(item); 9 | } 10 | 11 | dequeue() { 12 | return this.#items.shift(); 13 | } 14 | 15 | front() { 16 | return this.#items[0]; 17 | } 18 | 19 | isEmpty() { 20 | return this.#items.length === 0; 21 | } 22 | 23 | get size() { 24 | return this.#items.length; 25 | } 26 | 27 | clear() { 28 | this.#items = []; 29 | } 30 | 31 | toString() { 32 | if (this.isEmpty()) { 33 | return 'Empty Queue'; 34 | } else { 35 | return this.#items.map(item => { // {1} 36 | if (typeof item === 'object' && item !== null) { // {2} 37 | return JSON.stringify(item); // Handle objects 38 | } else { 39 | return item.toString(); // Handle other types {3} 40 | } 41 | }).join(', '); // {4} 42 | } 43 | } 44 | } 45 | 46 | module.exports = Queue; -------------------------------------------------------------------------------- /src/10-tree/leetcode/validate-binary-search-tree.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/validate-binary-search-tree/description/ 2 | // 98. Validate Binary Search Tree 3 | 4 | class TreeNode { 5 | val: number 6 | left: TreeNode | null 7 | right: TreeNode | null 8 | constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { 9 | this.val = (val === undefined ? 0 : val) 10 | this.left = (left === undefined ? null : left) 11 | this.right = (right === undefined ? null : right) 12 | } 13 | } 14 | 15 | function isValidBST(root: TreeNode | null): boolean { 16 | return isValid(root, null, null); 17 | } 18 | 19 | function isValid(node: TreeNode | null, min: number | null, max: number | null): boolean { 20 | if (!node) return true; 21 | if (min !== null && node.val <= min) return false; 22 | if (max !== null && node.val >= max) return false; 23 | return isValid(node.left, min, node.val) && isValid(node.right, node.val, max); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/04-stack/__test__/decimal-to-base.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | import decimalToBinary from '../decimal-to-binary'; 3 | 4 | describe('decimalToBinary', () => { 5 | test('should convert decimal number 0 to binary', () => { 6 | expect(decimalToBinary(0)).toBe('0'); 7 | }); 8 | 9 | test('should convert decimal number 1 to binary', () => { 10 | expect(decimalToBinary(1)).toBe('1'); 11 | }); 12 | 13 | test('should convert decimal number 10 to binary', () => { 14 | expect(decimalToBinary(10)).toBe('1010'); 15 | }); 16 | 17 | test('should convert decimal number 15 to binary', () => { 18 | expect(decimalToBinary(15)).toBe('1111'); 19 | }); 20 | 21 | test('should convert decimal number 100 to binary', () => { 22 | expect(decimalToBinary(100)).toBe('1100100'); 23 | }); 24 | 25 | test('should convert decimal number 255 to binary', () => { 26 | expect(decimalToBinary(255)).toBe('11111111'); 27 | }); 28 | }); -------------------------------------------------------------------------------- /src/04-stack/stack.ts: -------------------------------------------------------------------------------- 1 | // src/04-stack/stack.ts 2 | 3 | class Stack { 4 | private items: T[] = []; 5 | 6 | push(item: T): void { 7 | this.items.push(item); 8 | } 9 | 10 | pop(): T | undefined { 11 | return this.items.pop(); 12 | } 13 | 14 | peek(): T | undefined { 15 | return this.items[this.items.length - 1]; 16 | } 17 | 18 | isEmpty(): boolean { 19 | return this.items.length === 0; 20 | } 21 | 22 | get size(): number { 23 | return this.items.length; 24 | } 25 | 26 | clear(): void { 27 | this.items = []; 28 | } 29 | 30 | toString(): string { 31 | if (this.isEmpty()) { 32 | return 'Empty Stack'; 33 | } else { 34 | return this.items.map(item => { 35 | if (typeof item === 'object' && item !== null) { 36 | return JSON.stringify(item); 37 | } else { 38 | return item.toString(); 39 | } 40 | }).join(', '); 41 | } 42 | } 43 | } 44 | 45 | export default Stack; -------------------------------------------------------------------------------- /src/11-heap/03-heap-sort.js: -------------------------------------------------------------------------------- 1 | // src/11-heap/03-heap-sort.js 2 | 3 | const Comparator = require('../10-tree/comparator'); 4 | const Heap = require('./heap'); 5 | const HeapSort = require('./heap-sort'); 6 | 7 | const heapSort = (array, compareFn = Comparator.defaultCompareFn) => { 8 | const heap = new Heap(compareFn); 9 | heap.heapify(array); 10 | 11 | const sortedArray = []; 12 | while (!heap.isEmpty()){ 13 | sortedArray.push(heap.extract()); 14 | } 15 | 16 | 17 | return heap.toArray(); 18 | }; 19 | 20 | // using the Heap class and the heapify method 21 | let unsortedArray = [7, 6, 3, 5, 4, 1, 2]; 22 | console.log(heapSort(unsortedArray)); // [1, 2, 3, 4, 5, 6, 7] 23 | 24 | // using the heap sort algorithm 25 | const unsortedArray = [7, 6, 3, 5, 4, 1, 2]; 26 | HeapSort(unsortedArray); // will modify the array in place 27 | console.log(unsortedArray); // [1, 2, 3, 4, 5, 6, 7] 28 | 29 | // to see the output of this file use the command: node src/11-heap/03-heap-sort.js -------------------------------------------------------------------------------- /src/04-stack/__test__/decimal-to-binary.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | import decimalToBinary from '../decimal-to-binary'; 3 | 4 | describe('decimalToBinary', () => { 5 | test('should convert decimal number 0 to binary', () => { 6 | expect(decimalToBinary(0)).toBe('0'); 7 | }); 8 | 9 | test('should convert decimal number 1 to binary', () => { 10 | expect(decimalToBinary(1)).toBe('1'); 11 | }); 12 | 13 | test('should convert decimal number 10 to binary', () => { 14 | expect(decimalToBinary(10)).toBe('1010'); 15 | }); 16 | 17 | test('should convert decimal number 15 to binary', () => { 18 | expect(decimalToBinary(15)).toBe('1111'); 19 | }); 20 | 21 | test('should convert decimal number 100 to binary', () => { 22 | expect(decimalToBinary(100)).toBe('1100100'); 23 | }); 24 | 25 | test('should convert decimal number 255 to binary', () => { 26 | expect(decimalToBinary(255)).toBe('11111111'); 27 | }); 28 | }); -------------------------------------------------------------------------------- /src/08-dictionary-hash/04-using-hashmap-class.js: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/04-using-hashmap-class.js 2 | 3 | const HashTable = require('./hash-table'); 4 | 5 | const addressBook = new HashTable(); 6 | 7 | // Add contacts 8 | addressBook.put('Gandalf', 'gandalf@email.com'); 9 | addressBook.put('John', 'johnsnow@email.com'); 10 | addressBook.put('Tyrion', 'tyrion@email.com'); 11 | 12 | // Retrieve the hash code of a contact 13 | console.log(addressBook.hash('Gandalf')); // 19 14 | console.log(addressBook.hash('John')); // 29 15 | console.log(addressBook.hash('Tyrion')); // 16 16 | 17 | // Retrieve contacts 18 | console.log(addressBook.get('Gandalf')); // gandalf@email.com 19 | console.log(addressBook.get('Loiane')); // undefined 20 | 21 | // Remove contacts 22 | console.log(addressBook.remove('Gandalf')); // true 23 | console.log(addressBook.get('Gandalf')); // undefined 24 | 25 | 26 | // to see the output of this file use the command: node src/08-dictionary-hash/04-using-hashmap-class.js -------------------------------------------------------------------------------- /src/01-intro/03-loops.js: -------------------------------------------------------------------------------- 1 | // Path: src/01-intro/03-loops.js 2 | 3 | console.log('**** for example ****'); 4 | /* for - example */ 5 | for (let i = 0; i < 10; i++) { 6 | console.log(i); 7 | } 8 | 9 | console.log('**** while example ****'); 10 | /* while - example */ 11 | let i = 0; 12 | while (i < 10) { 13 | console.log(i); 14 | i++; 15 | } 16 | 17 | console.log('**** do-while example ****'); 18 | /* do-while - example */ 19 | i = 0; 20 | do { 21 | console.log(i); 22 | i++; 23 | } while (i < 10); 24 | 25 | console.log('**** for-in example ****'); 26 | /* for-in - example */ 27 | const obj = { a: 1, b: 2, c: 3 }; 28 | for (const key in obj) { 29 | console.log(key, obj[key]); 30 | } 31 | // output: a 1 b 2 c 3 32 | 33 | console.log('**** for-of example ****'); 34 | /* for-of - example */ 35 | const arr = [1, 2, 3]; 36 | for (const value of arr) { 37 | console.log(value); 38 | } 39 | // output: 1 2 3 40 | 41 | // to see the output of this file use the command: node src/01-intro/03-loops.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Learning JavaScript Data Structures and Algorithms 2 | =================================================== 3 | 4 | # Project Structure 5 | 6 | You can find the source code organized by chapter under the `src` folder. 7 | 8 | *Each file has the `.js` and the `.ts` extension, so you get both the JavaScript and the Typescript versions of the source code.* 9 | 10 | *Each data structure and algorithm from parts 2 and 3 also have a `_test_`* folder where you can find the respective `Jest` test cases. 11 | 12 | ## How to use this repository 13 | 14 | **Install all dependencies** 15 | 16 | ``` 17 | npm install 18 | ``` 19 | 20 | **Run all tests** 21 | 22 | ``` 23 | npm test 24 | ``` 25 | 26 | **Run a particular example** 27 | 28 | ``` 29 | cd src/01-intro 30 | node 01-hello-variables.js 31 | ``` 32 | 33 | or: 34 | 35 | ``` 36 | node src/01-intro/01-hello-variables.js 37 | ``` 38 | 39 | ## 💻 Tecnologies 40 | 41 | * JavaScript 42 | * TypeScript 43 | * Jest (tests) 44 | 45 | Happy Coding! 46 | -------------------------------------------------------------------------------- /src/03-array/hackerrank/01-arrays-ds.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/hackerrank/01-arrays-ds.js 2 | // Problem: https://www.hackerrank.com/challenges/arrays-ds/problem 3 | // Solution 1 4 | function reverseArray(a) { 5 | return a.reverse(); 6 | } 7 | // Solution 2 8 | function reverseArray2(a) { 9 | const result = []; 10 | for (let i = a.length - 1; i >= 0; i--) { 11 | result.push(a[i]); 12 | } 13 | return result; 14 | } 15 | // Solution 3 16 | function reverseArray3(a) { 17 | return a.map((_, i) => a[a.length - 1 - i]); 18 | } 19 | // Solution 4 20 | function reverseArray4(a) { 21 | const result = []; 22 | for (let i = 0; i < a.length; i++) { 23 | result.unshift(a[i]); 24 | } 25 | return result; 26 | } 27 | // Test cases 28 | console.log(reverseArray([1, 4, 3, 2])); // [2, 3, 4, 1] 29 | console.log(reverseArray2([1, 4, 3, 2])); // [2, 3, 4, 1] 30 | console.log(reverseArray3([1, 4, 3, 2])); // [2, 3, 4, 1] 31 | console.log(reverseArray4([1, 4, 3, 2])); // [2, 3, 4, 1] 32 | -------------------------------------------------------------------------------- /src/04-stack/02-base-converter-examples.ts: -------------------------------------------------------------------------------- 1 | // src/04-stack/03-base-converter-examples.ts 2 | 3 | import decimalToBinary from './decimal-to-binary'; 4 | import { decimalToBase, decimalToBase64 } from './decimal-to-base'; 5 | 6 | // Decimal to binary 7 | console.log(decimalToBinary(0)); // 0 8 | console.log(decimalToBinary(1)); // 1 9 | console.log(decimalToBinary(2)); // 10 10 | console.log(decimalToBinary(13)); // 1101 11 | console.log(decimalToBinary(233)); // 11101001 12 | console.log(decimalToBinary(10)); // 1010 13 | console.log(decimalToBinary(1000)); // 1111101000 14 | 15 | // Decimal to base 16 | console.log(decimalToBase(100345, 2)); // 11000011111111001 17 | console.log(decimalToBase(100345, 8)); // 303771 18 | console.log(decimalToBase(100345, 16)); // 187F9 19 | console.log(decimalToBase(100345, 35)); // 2BW0 20 | 21 | // Decimal to base 64 22 | console.log(decimalToBase64(100345, 64)); // Yf5= 23 | 24 | // to see the output of this file use the command: npx ts-node src/04-stack/03-base-converter-examples.js -------------------------------------------------------------------------------- /src/07-set/leetcode/remove-duplicates-from-sorted-array.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/remove-duplicates-from-sorted-array/description/ 2 | 3 | /** 4 | * @param {number[]} nums 5 | * @return {number} 6 | */ 7 | export function removeDuplicates(nums: number[]): number { 8 | if (nums.length === 0) { 9 | return 0; 10 | } 11 | let i = 0; 12 | for (let j = 1; j < nums.length; j++) { 13 | if (nums[j] !== nums[i]) { 14 | i++; 15 | nums[i] = nums[j]; 16 | } 17 | } 18 | return i + 1; 19 | } 20 | 21 | // resolve this problem using Set 22 | export function removeDuplicates2(nums: number[]): number { 23 | const set = new Set(nums); 24 | const arr = Array.from(set); 25 | for (let i = 0; i < arr.length; i++) { 26 | nums[i] = arr[i]; 27 | } 28 | return arr.length; 29 | } 30 | 31 | // explain above solution 32 | // 1. create a set from the array 33 | // 2. create an array from the set 34 | // 3. copy the array to the original array 35 | // 4. return the length of the array 36 | // Time complexity: O(n) -------------------------------------------------------------------------------- /src/10-tree/fenwick-tree.js: -------------------------------------------------------------------------------- 1 | // Binary Indexed Tree (Fenwick Tree) 2 | class FenwickTree { 3 | #arraySize; 4 | #tree; 5 | 6 | constructor(arraySize) { 7 | this.#arraySize = arraySize; 8 | this.#tree = Array(arraySize + 1).fill(0); 9 | } 10 | 11 | update(index, value) { 12 | if (index < 1 || index > this.#arraySize) { 13 | throw new Error('Index is out of range'); 14 | } 15 | 16 | for (let i = index; i <= this.#arraySize; i += this.#lowBit(i)) { 17 | this.#tree[i] += value; 18 | } 19 | } 20 | 21 | query(index) { 22 | if (index < 1 || index > this.#arraySize) { 23 | throw new Error('Index is out of range'); 24 | } 25 | 26 | let sum = 0; 27 | 28 | for (let i = index; i > 0; i -= this.#lowBit(i)) { 29 | sum += this.#tree[i]; 30 | } 31 | 32 | return sum; 33 | } 34 | 35 | #lowBit(x) { 36 | return x & -x; 37 | } 38 | 39 | get arraySize() { 40 | return this.#arraySize; 41 | } 42 | 43 | toString() { 44 | return this.#tree.join(', '); 45 | } 46 | } -------------------------------------------------------------------------------- /src/04-stack/02-base-converter-examples.js: -------------------------------------------------------------------------------- 1 | // src/04-stack/03-base-converter-examples.js 2 | 3 | const decimalToBinary = require('./decimal-to-binary'); 4 | const converters = require('./decimal-to-base'); 5 | 6 | // Decimal to binary 7 | console.log(decimalToBinary(0)); // 0 8 | console.log(decimalToBinary(1)); // 1 9 | console.log(decimalToBinary(2)); // 10 10 | console.log(decimalToBinary(13)); // 1101 11 | console.log(decimalToBinary(233)); // 11101001 12 | console.log(decimalToBinary(10)); // 1010 13 | console.log(decimalToBinary(1000)); // 1111101000 14 | 15 | // Decimal to base 16 | console.log(converters.decimalToBase(100345, 2)); // 11000011111111001 17 | console.log(converters.decimalToBase(100345, 8)); // 303771 18 | console.log(converters.decimalToBase(100345, 16)); // 187F9 19 | console.log(converters.decimalToBase(100345, 35)); // 2BW0 20 | 21 | // Decimal to base 64 22 | console.log(converters.decimalToBase64(100345, 64)); // Yf5= 23 | 24 | // to see the output of this file use the command: node src/04-stack/03-base-converter-examples.js -------------------------------------------------------------------------------- /src/01-intro/05-scope.js: -------------------------------------------------------------------------------- 1 | // Path: src/01-intro/05-scope.js 2 | 3 | let movie = 'Lord of the Rings'; 4 | 5 | function starWarsFan() { 6 | const movie = 'Star Wars'; 7 | return movie; 8 | } 9 | 10 | function marvelFan() { 11 | movie = 'The Avengers'; 12 | return movie; 13 | } 14 | 15 | console.log(movie); // Lord of the Rings 16 | console.log(starWarsFan()); // Star Wars 17 | console.log(marvelFan()); // The Avengers 18 | console.log(movie); // The Avengers 19 | 20 | // block scope 21 | function blizzardFan() { 22 | const isFan = true; 23 | let phrase = 'Warcraft'; 24 | console.log('Before if: ' + phrase); 25 | if (isFan) { 26 | let phrase = 'initial text'; 27 | phrase = 'For the Horde!'; 28 | console.log('Inside if: ' + phrase); 29 | } 30 | phrase = 'For the Alliance!'; 31 | console.log('After if: ' + phrase); 32 | } 33 | 34 | blizzardFan(); 35 | // Before if: Warcraft 36 | // Inside if: For the Horde! 37 | // After if: For the Alliance! 38 | 39 | // to see the output of this file use the command: node src/01-intro/05-scope.js 40 | -------------------------------------------------------------------------------- /src/10-tree/01-bst.js: -------------------------------------------------------------------------------- 1 | // src/10-tree/01-bst.js 2 | const BinarySearchTree = require('./binary-search-tree'); 3 | 4 | class Student { 5 | constructor(idNumber, name, gradeLevel, address) { 6 | this.idNumber = idNumber; 7 | this.name = name; 8 | this.gradeLevel = gradeLevel; 9 | } 10 | } 11 | 12 | // create student comparator to compare idNumber 13 | const studentComparator = (a, b) => a.idNumber - b.idNumber; 14 | 15 | const studentTree = new BinarySearchTree(studentComparator); 16 | 17 | studentTree.insert(new Student(11, 'Darcy', 10)); 18 | studentTree.insert(new Student(7, 'Tory', 10)); 19 | studentTree.insert(new Student(5, 'Caleb', 10)); 20 | studentTree.insert(new Student(9, 'Sofia', 10)); 21 | studentTree.insert(new Student(15, 'Max', 10)); 22 | 23 | // 11 24 | // / \ 25 | // 7 15 26 | // / \ 27 | // 5 9 28 | 29 | studentTree.insert(new Student(12, 'Seth', 10)); 30 | 31 | // 11 32 | // / \ 33 | // 7 15 34 | // / \ / 35 | // 5 9 12 36 | 37 | 38 | // to see the output of this file use the command: node src/10-tree/01-bst.js 39 | -------------------------------------------------------------------------------- /src/11-heap/02-max-heap.js: -------------------------------------------------------------------------------- 1 | // src/11-heap/02-max-heap.js 2 | 3 | const Heap = require('./heap'); 4 | 5 | class Patient { 6 | constructor(name, priority) { 7 | this.name = name; 8 | this.priority = priority; 9 | } 10 | } 11 | 12 | const PRIORITY = { LOW: 1, MEDIUM: 2, HIGH: 3 }; 13 | 14 | const erHeap = new Heap((a, b) => b.priority - a.priority); 15 | 16 | erHeap.insert(new Patient('Poppy', PRIORITY.LOW)); 17 | erHeap.insert(new Patient('Kieran', PRIORITY.HIGH)); 18 | erHeap.insert(new Patient('Camila', PRIORITY.MEDIUM)); 19 | erHeap.insert(new Patient('Casteel', PRIORITY.LOW)); 20 | erHeap.insert(new Patient('Mike', PRIORITY.HIGH)); 21 | 22 | console.log('Head', erHeap.toArray()); 23 | 24 | while (!erHeap.isEmpty()) { 25 | const patient = erHeap.extract(); 26 | console.log('Next patient:', patient.name, patient.priority); 27 | } 28 | // Next patient: Kieran 3 29 | // Next patient: Mike 3 30 | // Next patient: Camila 2 31 | // Next patient: Casteel 1 32 | // Next patient: Poppy 1 33 | 34 | // to see the output of this file use the command: node src/11-heap/02-max-heap.js 35 | -------------------------------------------------------------------------------- /src/10-tree/05-avl.js: -------------------------------------------------------------------------------- 1 | // src/10-tree/05-avl.js 2 | 3 | const AVLTree = require('./avl-tree'); 4 | 5 | class Student { 6 | constructor(idNumber, name, gradeLevel, address) { 7 | this.idNumber = idNumber; 8 | this.name = name; 9 | this.gradeLevel = gradeLevel; 10 | } 11 | toString() { 12 | return `${this.idNumber} - ${this.name}`; 13 | } 14 | } 15 | 16 | // create student comparator to compare idNumber 17 | const studentComparator = (a, b) => a.idNumber - b.idNumber; 18 | 19 | const studentTree = new AVLTree(studentComparator); 20 | 21 | studentTree.insert(new Student(11, 'Darcy', 10)); 22 | studentTree.insert(new Student(7, 'Tory', 10)); 23 | studentTree.insert(new Student(5, 'Caleb', 10)); 24 | studentTree.insert(new Student(9, 'Sofia', 10)); 25 | studentTree.insert(new Student(15, 'Max', 10)); 26 | studentTree.insert(new Student(13, 'Geraldine', 10)); 27 | studentTree.insert(new Student(12, 'Seth', 10)); 28 | 29 | 30 | // 11 31 | // / \ 32 | // 7 13 33 | // / \ / \ 34 | // 5 9 12 15 35 | 36 | // to see the output of this file use the command: node src/10-tree/05-avl.js -------------------------------------------------------------------------------- /src/05-queue-deque/02-using-queue-two-pointers.js: -------------------------------------------------------------------------------- 1 | // src/05-queue-deque/02-using-queue-two-pointers.js 2 | 3 | const Queue = require('./queue-two-pointers'); 4 | 5 | const queue = new Queue(); 6 | 7 | console.log(queue.isEmpty()); // true 8 | 9 | queue.enqueue({ document: 'Chapter05.docx', pages: 20 }); 10 | queue.enqueue({ document: 'JavaScript.pdf', pages: 60 }); 11 | queue.enqueue({ document: 'TypeScript.pdf', pages: 80 }); 12 | 13 | console.log(queue.toString()); 14 | 15 | console.log(queue.size); // 3 16 | 17 | console.log(queue.isEmpty()); // false 18 | 19 | console.log(queue.front()); // { document: 'Chapter05.docx', pages: 20 } 20 | 21 | // print all documents 22 | while (!queue.isEmpty()) { 23 | console.log(queue.dequeue()); 24 | } 25 | 26 | // add more documents 27 | queue.enqueue({ document: 'DataStructures.pdf', pages: 400 }); 28 | 29 | console.log(queue.toString()); 30 | console.log(queue.size); // 1 31 | console.log(queue.front()); // { document: 'DataStructures.pdf', pages: 400 } 32 | 33 | // to see the output of this file use the command: node src/05-queue-deque/02-using-queue-two-pointers.js -------------------------------------------------------------------------------- /src/09-recursion/leetcode/power-of-two.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/power-of-two/ 2 | 3 | // function isPowerOfTwo(n: number): boolean { 4 | // if (n <= 0) { 5 | // return false; 6 | // } 7 | 8 | // return (n & (n - 1)) === 0; 9 | // } 10 | 11 | // Time complexity: O(1) 12 | // Space complexity: O(1) 13 | 14 | // Test cases 15 | 16 | 17 | // recursive solution 18 | function isPowerOfTwo(n: number): boolean { 19 | if (n <= 0) { // edge case for negative numbers 20 | return false; 21 | } 22 | if (n % 2 !== 0) { // edge case for odd numbers 23 | return false; 24 | } 25 | 26 | if (n === 1) { // base case 27 | return true; 28 | } 29 | 30 | return isPowerOfTwo(n / 2); 31 | } 32 | 33 | // Time complexity: O(log n) 34 | // Space complexity: O(log n) 35 | 36 | // Test cases 37 | console.log(isPowerOfTwo(1)); // true 38 | console.log(isPowerOfTwo(16)); // true 39 | console.log(isPowerOfTwo(3)); // false 40 | console.log(isPowerOfTwo(4)); // true 41 | console.log(isPowerOfTwo(5)); // false 42 | console.log(isPowerOfTwo(0)); // false 43 | console.log(isPowerOfTwo(-1)); // false 44 | -------------------------------------------------------------------------------- /src/07-set/02-using-set-class.js: -------------------------------------------------------------------------------- 1 | // src/07-set/02-using-set-class.js 2 | 3 | const article = { 4 | title: 'The importance of data structures in programming', 5 | content: '...', 6 | tags: new Set() 7 | tags: new Set(['programming', 'data structures', 'algorithms']) 8 | }; 9 | 10 | // add tags 11 | article.tags.add('programming'); 12 | article.tags.add('data structures'); 13 | article.tags.add('algorithms'); 14 | article.tags.add('programming'); 15 | 16 | console.log(article.tags.size); // 3 17 | console.log(article.tags.has('data structures')); // true 18 | console.log(article.tags.has('algorithms')); // true 19 | console.log(article.tags.has('programming')); // true 20 | console.log(article.tags.has('javascript')); // false 21 | console.log(article.tags.values()); // ['programming', 'data structures', 'algorithms'] 22 | 23 | // remove tags 24 | article.tags.delete('programming'); 25 | article.tags.add('JavaScript'); 26 | console.log(article.tags.values()); // ['data structures', 'algorithms', 'JavaScript'] 27 | 28 | 29 | // to see the output of this file use the command: node src/07-set/02-using-set-class.js -------------------------------------------------------------------------------- /src/04-stack/01-undo-feature.js: -------------------------------------------------------------------------------- 1 | // src/04-stack/01-undo-feature.js 2 | 3 | const Stack = require('./stack'); 4 | // import { Stack } from './stack'; // or './stack.js' if you are using ES modules 5 | 6 | const undoFeature = new Stack(); 7 | 8 | console.log(undoFeature.isEmpty()); // true 9 | 10 | undoFeature.push({action: 'typing', text: 'S'}); 11 | undoFeature.push({action: 'typing', text: 't'}); 12 | 13 | console.log(undoFeature.peek()); // { action: 'typing', text: 't' } 14 | 15 | console.log(undoFeature.size); // 2 16 | 17 | undoFeature.push({action: 'typing', text: 'a'}); 18 | undoFeature.push({action: 'typing', text: 'c'}); 19 | undoFeature.push({action: 'typing', text: 'k'}); 20 | 21 | console.log(undoFeature.size); // 5 22 | console.log(undoFeature.isEmpty()); // false 23 | 24 | // removing two elements from the stack 25 | undoFeature.pop(); 26 | undoFeature.pop(); 27 | 28 | console.log(undoFeature.size); // 3 29 | console.log(undoFeature.peek()); // { action: 'typing', text: 'a' } 30 | 31 | // toString 32 | console.log(undoFeature.toString()); 33 | 34 | // to see the output of this file use the command: node src/04-stack/01-undo-feature.js -------------------------------------------------------------------------------- /src/05-queue-deque/queue.ts: -------------------------------------------------------------------------------- 1 | // src/05-queue-deque/queue.ts 2 | 3 | class Queue { 4 | 5 | private items: T[] = []; 6 | 7 | enqueue(item: T): void { 8 | this.items.push(item); 9 | } 10 | 11 | dequeue(): T | undefined { 12 | if (this.isEmpty()) { 13 | return undefined; 14 | } 15 | return this.items.shift(); 16 | } 17 | 18 | front(): T | undefined { 19 | if (this.isEmpty()) { 20 | return undefined; 21 | } 22 | return this.items[0]; 23 | } 24 | 25 | isEmpty(): boolean { 26 | return this.items.length === 0; 27 | } 28 | 29 | get size(): number { 30 | return this.items.length; 31 | } 32 | 33 | clear(): void { 34 | this.items = []; 35 | } 36 | 37 | toString(): string { 38 | if (this.isEmpty()) { 39 | return 'Empty Queue'; 40 | } else { 41 | return this.items.map(item => { // {1} 42 | if (typeof item === 'object' && item !== null) { // {2} 43 | return JSON.stringify(item); // Handle objects 44 | } else { 45 | return item.toString(); // Handle other types {3} 46 | } 47 | }).join(', '); // {4} 48 | } 49 | } 50 | } 51 | 52 | export default Queue; -------------------------------------------------------------------------------- /src/13-graph/floyd-warshall.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/floyd-warshall.js 2 | 3 | const INF = Number.MAX_SAFE_INTEGER; // Infinity 4 | 5 | const initializeMatrix = (graph) => { 6 | const dist = []; 7 | const { length } = graph; 8 | for (let i = 0; i < length; i++) { 9 | dist[i] = []; 10 | for (let j = 0; j < length; j++) { 11 | if (i === j) { 12 | dist[i][j] = 0; 13 | } else if (!isFinite(graph[i][j])) { 14 | dist[i][j] = INF; 15 | } else { 16 | dist[i][j] = graph[i][j]; 17 | } 18 | } 19 | } 20 | return dist; 21 | } 22 | 23 | const floydWarshall = (graph) => { 24 | const { length } = graph; 25 | const dist = initializeMatrix(graph); 26 | 27 | // Consider each airport as an intermediate point 28 | for (let k = 0; k < length; k++) { 29 | for (let i = 0; i < length; i++) { 30 | for (let j = 0; j < length; j++) { 31 | // If a shorter path is found through an intermediate airport, update the distance 32 | if (dist[i][k] + dist[k][j] < dist[i][j]) { 33 | dist[i][j] = dist[i][k] + dist[k][j]; 34 | } 35 | } 36 | } 37 | } 38 | return dist; 39 | }; 40 | 41 | module.exports = floydWarshall; -------------------------------------------------------------------------------- /src/10-tree/06-red-black.js: -------------------------------------------------------------------------------- 1 | // src/10-tree/06-red-black.js 2 | 3 | const RedBlackTree = require('./red-black-tree'); 4 | 5 | class Student { 6 | constructor(idNumber, name, gradeLevel, address) { 7 | this.idNumber = idNumber; 8 | this.name = name; 9 | this.gradeLevel = gradeLevel; 10 | } 11 | toString() { 12 | return `${this.idNumber} - ${this.name}`; 13 | } 14 | } 15 | 16 | // create student comparator to compare idNumber 17 | const studentComparator = (a, b) => a.idNumber - b.idNumber; 18 | 19 | const studentTree = new RedBlackTree(studentComparator); 20 | 21 | studentTree.insert(new Student(11, 'Darcy', 10)); 22 | studentTree.insert(new Student(7, 'Tory', 10)); 23 | studentTree.insert(new Student(5, 'Caleb', 10)); 24 | studentTree.insert(new Student(9, 'Sofia', 10)); 25 | studentTree.insert(new Student(15, 'Max', 10)); 26 | studentTree.insert(new Student(13, 'Geraldine', 10)); 27 | studentTree.insert(new Student(12, 'Seth', 10)); 28 | 29 | studentTree.print(); 30 | 31 | // console.log('--- Removing 7'); 32 | // studentTree.remove(new Student(7, 'Tory', 10)); 33 | // studentTree.print(); 34 | 35 | // to see the output of this file use the command: node src/10-tree/06-red-black.js -------------------------------------------------------------------------------- /src/05-queue-deque/deque.js: -------------------------------------------------------------------------------- 1 | // src/05-queue-deque/deque.js 2 | 3 | class Deque { 4 | #items = []; 5 | 6 | addFront(item) { 7 | this.#items.unshift(item); 8 | } 9 | 10 | addRear(item) { 11 | this.#items.push(item); 12 | } 13 | 14 | removeFront() { 15 | return this.#items.shift(); 16 | } 17 | 18 | removeRear() { 19 | return this.#items.pop(); 20 | } 21 | 22 | peekFront() { 23 | return this.#items[0]; 24 | } 25 | 26 | peekRear() { 27 | return this.#items[this.#items.length - 1]; 28 | } 29 | 30 | isEmpty() { 31 | return this.#items.length === 0; 32 | } 33 | 34 | get size() { 35 | return this.#items.length; 36 | } 37 | 38 | clear() { 39 | this.#items = []; 40 | } 41 | 42 | toString() { 43 | if (this.isEmpty()) { 44 | return 'Empty Deque'; 45 | } else { 46 | return this.#items.map(item => { // {1} 47 | if (typeof item === 'object' && item !== null) { // {2} 48 | return JSON.stringify(item); // Handle objects 49 | } else { 50 | return item.toString(); // Handle other types {3} 51 | } 52 | }).join(', '); // {4} 53 | } 54 | } 55 | } 56 | 57 | module.exports = Deque; -------------------------------------------------------------------------------- /src/11-heap/01-min-heap.js: -------------------------------------------------------------------------------- 1 | // src/11-heap/01-min-heap.js 2 | 3 | const Heap = require('./heap'); 4 | 5 | class Task { 6 | constructor(id, priority, executionTime) { 7 | this.id = id; 8 | this.priority = priority; 9 | this.executionTime = executionTime; 10 | } 11 | } 12 | 13 | class TaskScheduler { 14 | constructor() { 15 | this.taskQueue = new Heap((a,b) => a.priority - b.priority); 16 | } 17 | 18 | addTask(task) { 19 | this.taskQueue.insert(task); 20 | } 21 | 22 | scheduleNextTask() { 23 | if (this.taskQueue.isEmpty()) { 24 | return null; // No tasks to schedule 25 | } 26 | return this.taskQueue.extract(); 27 | } 28 | } 29 | 30 | const scheduler = new TaskScheduler(); 31 | scheduler.addTask(new Task(2, 2, 10)); 32 | scheduler.addTask(new Task(3, 3, 5)); 33 | scheduler.addTask(new Task(4, 4, 8)); 34 | scheduler.addTask(new Task(5, 5, 15)); 35 | scheduler.addTask(new Task(1, 1, 20)); 36 | 37 | console.log(scheduler.taskQueue.toArray()); 38 | 39 | console.log(scheduler.scheduleNextTask()); // Output: Task 1 (highest priority) 40 | 41 | console.log(scheduler.taskQueue.toArray()); 42 | 43 | // to see the output of this file use the command: node src/11-heap/01-min-heap.js 44 | -------------------------------------------------------------------------------- /src/10-tree/leetcode/binary-tree-preorder-traversal.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/binary-tree-preorder-traversal/ 2 | // 144. Binary Tree Preorder Traversal 3 | 4 | class TreeNode { 5 | val: number 6 | left: TreeNode | null 7 | right: TreeNode | null 8 | constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { 9 | this.val = (val === undefined ? 0 : val) 10 | this.left = (left === undefined ? null : left) 11 | this.right = (right === undefined ? null : right) 12 | } 13 | } 14 | 15 | function preorderTraversal(root: TreeNode | null): number[] { 16 | // Recursive 17 | //if (!root) return []; 18 | //return [root.val, ...preorderTraversal(root.left), ...preorderTraversal(root.right)]; 19 | 20 | // Iterative 21 | const stack: TreeNode[] = []; 22 | const result: number[] = []; 23 | let current = root; 24 | while (stack.length !== 0 || current !== null) { 25 | if (current) { 26 | result.push(current.val); 27 | if (current.right) stack.push(current.right); 28 | current = current.left; 29 | } else { 30 | current = stack.pop(); 31 | } 32 | } 33 | return result; 34 | }; 35 | 36 | // Time complexity: O(n) 37 | // Space complexity: O(n) (worst case) - O(log n) (average case) -------------------------------------------------------------------------------- /src/13-graph/graph.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/graph.js 2 | 3 | // Graph class 4 | class Graph { 5 | 6 | #isDirected = false; 7 | #vertices = []; 8 | #adjList = new Map(); 9 | 10 | constructor(isDirected = false) { 11 | this.#isDirected = isDirected; 12 | } 13 | 14 | addVertex(vertex) { 15 | if (!this.#vertices.includes(vertex)) { 16 | this.#vertices.push(vertex); 17 | this.#adjList.set(vertex, []); 18 | } 19 | } 20 | 21 | addEdge(vertex, edge) { 22 | if (!this.#adjList.get(vertex)) { 23 | this.addVertex(vertex); 24 | } 25 | if (!this.#adjList.get(edge)) { 26 | this.addVertex(edge); 27 | } 28 | this.#adjList.get(vertex).push(edge); 29 | if (!this.#isDirected) { 30 | this.#adjList.get(edge).push(vertex); 31 | } 32 | } 33 | 34 | get vertices() { 35 | return this.#vertices; 36 | } 37 | 38 | get adjList() { 39 | return this.#adjList; 40 | } 41 | 42 | toString() { 43 | let s = ''; 44 | this.#vertices.forEach(vertex => { 45 | s += `${vertex} -> `; 46 | this.#adjList.get(vertex).forEach(neighbor => { 47 | s += `${neighbor} `; 48 | }); 49 | s += '\n'; 50 | }); 51 | return s; 52 | } 53 | } 54 | 55 | module.exports = Graph; -------------------------------------------------------------------------------- /src/10-tree/leetcode/binary-tree-inorder-traversal.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/binary-tree-inorder-traversal/ 2 | // 94. Binary Tree Inorder Traversal 3 | 4 | class TreeNode { 5 | val: number 6 | left: TreeNode | null 7 | right: TreeNode | null 8 | constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { 9 | this.val = (val === undefined ? 0 : val) 10 | this.left = (left === undefined ? null : left) 11 | this.right = (right === undefined ? null : right) 12 | } 13 | } 14 | 15 | function inorderTraversal(root: TreeNode | null): number[] { 16 | // Recursive 17 | //if (!root) return []; 18 | //return [...inorderTraversal(root.left), root.val, ...inorderTraversal(root.right)]; 19 | 20 | // Iterative 21 | const stack: TreeNode[] = []; 22 | const result: number[] = []; 23 | let current = root; 24 | while (stack.length !== 0 || current !== null) { 25 | if (current) { 26 | stack.push(current) 27 | current = current.left; 28 | } else { 29 | current = stack.pop(); 30 | result.push(current.val); 31 | current = current.right; 32 | } 33 | } 34 | return result; 35 | }; 36 | 37 | // Time complexity: O(n) 38 | // Space complexity: O(n) (worst case) - O(log n) (average case) 39 | -------------------------------------------------------------------------------- /src/03-array/08-different-data-types.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/07-different-data-types.js 2 | 3 | const legacyOrderData = '12345,Cassian,2024-01-21,Laptop:1200|Mouse:50,1250,Shipped'; 4 | const customerOrderArray = legacyOrderData.split(','); 5 | console.log(customerOrderArray); 6 | // [ 7 | // '12345', 8 | // 'Cassian', 9 | // '2024-01-21', 10 | // 'Laptop:1200|Mouse:50', 11 | // '1250', 12 | // 'Shipped' 13 | // ] 14 | 15 | const customerOrder = { 16 | id: Number(customerOrderArray[0]), 17 | name: customerOrderArray[1], 18 | date: new Date(customerOrderArray[2]), 19 | items: customerOrderArray[3].split('|').map(item => { 20 | const [name, price] = item.split(':'); 21 | return { 22 | name, 23 | price: Number(price) 24 | }; 25 | }), 26 | total: Number(customerOrderArray[4]), 27 | status: customerOrderArray[5] 28 | }; 29 | console.log(customerOrder); 30 | // { 31 | // id: 12345, 32 | // name: 'Cassian', 33 | // date: 2024-01-21T00:00:00.000Z, 34 | // items: [ { name: 'Laptop', price: 1200 }, { name: 'Mouse', price: 50 } ], 35 | // total: 1250, 36 | // status: 'Shipped' 37 | // } 38 | 39 | // to see the output of this file use the command: node src/03-array/07-different-data-types.js -------------------------------------------------------------------------------- /src/04-stack/01-using-stack-class.ts: -------------------------------------------------------------------------------- 1 | // src/04-stack/01-using-stack-class.ts 2 | 3 | import Stack from './stack'; 4 | 5 | enum Action { 6 | TYPE = 'typing', 7 | // Add more actions like DELETE, FORMAT, etc. 8 | } 9 | 10 | interface EditorAction { 11 | action: Action; 12 | text?: string; // Optional text for typing actions 13 | } 14 | 15 | const stack = new Stack(); 16 | 17 | console.log(stack.isEmpty()); // true 18 | 19 | console.log(stack.peek()); // undefined 20 | 21 | stack.push({action: Action.TYPE, text: 'S'}); 22 | stack.push({action: Action.TYPE, text: 't'}); 23 | 24 | console.log(stack.peek()); // { action: 'typing', text: 't' } 25 | 26 | console.log(stack.size); // 2 27 | 28 | stack.push({action: Action.TYPE, text: 'a'}); 29 | stack.push({action: Action.TYPE, text: 'c'}); 30 | stack.push({action: Action.TYPE, text: 'k'}); 31 | 32 | console.log(stack.size); // 5 33 | console.log(stack.isEmpty()); // false 34 | 35 | // removing two elements from the stack 36 | stack.pop(); 37 | stack.pop(); 38 | 39 | console.log(stack.size); // 3 40 | console.log(stack.peek()); // { action: 'typing', text: 'a' } 41 | 42 | // toString 43 | console.log(stack.toString()); 44 | 45 | // to see the output of this file use the command: npx ts-node src/04-stack/01-using-stack-class.ts -------------------------------------------------------------------------------- /src/01-intro/02-conditionals.js: -------------------------------------------------------------------------------- 1 | // Path: src/01-intro/02-conditionals.js 2 | 3 | /* Example 01 - if-else */ 4 | let number = 0; 5 | if (number === 1) { 6 | console.log('number is equal to 1'); 7 | } else { 8 | console.log('number is not equal to 1, the value of number is ' + number); 9 | } 10 | 11 | /* Example 02 - ternary operator - if..else */ 12 | if (number === 1) { 13 | number--; 14 | } else { 15 | number++; 16 | } 17 | 18 | // is the same as 19 | number === 1 ? number-- : number++; 20 | 21 | // is the same as 22 | number = number === 1 ? number - 1 : number + 1; 23 | 24 | /* Example 03 - if-else-if-else... */ 25 | let month = 5; 26 | if (month === 1) { 27 | console.log('January'); 28 | } else if (month === 2) { 29 | console.log('February'); 30 | } else if (month === 3) { 31 | console.log('March'); 32 | } else { 33 | console.log('Month is not January, February or March'); 34 | } 35 | 36 | /* Example 05 - switch */ 37 | switch (month) { 38 | case 1: 39 | console.log('January'); 40 | break; 41 | case 2: 42 | console.log('February'); 43 | break; 44 | case 3: 45 | console.log('March'); 46 | break; 47 | default: 48 | console.log('Month is not January, February or March'); 49 | } 50 | 51 | // to see the output of this file use the command: node src/01-intro/02-conditionals.js -------------------------------------------------------------------------------- /src/13-graph/leetcode/number-of-islands.ts: -------------------------------------------------------------------------------- 1 | // 200. Number of Islands 2 | // https://leetcode.com/problems/number-of-islands/ 3 | 4 | function numIslands(grid: string[][]): number { 5 | let count = 0; 6 | for (let i = 0; i < grid.length; i++) { 7 | for (let j = 0; j < grid[0].length; j++) { 8 | if (grid[i][j] === "1") { 9 | count++; 10 | dfs(grid, i, j); 11 | } 12 | } 13 | } 14 | return count; 15 | } 16 | 17 | function dfs(grid: string[][], i: number, j: number) { 18 | if ( 19 | i < 0 || 20 | j < 0 || 21 | i >= grid.length || 22 | j >= grid[0].length || 23 | grid[i][j] === "0" 24 | ) { 25 | return; 26 | } 27 | grid[i][j] = "0"; 28 | dfs(grid, i + 1, j); 29 | dfs(grid, i - 1, j); 30 | dfs(grid, i, j + 1); 31 | dfs(grid, i, j - 1); 32 | } 33 | 34 | console.log( 35 | numIslands([ 36 | ["1", "1", "1", "1", "0"], 37 | ["1", "1", "0", "1", "0"], 38 | ["1", "1", "0", "0", "0"], 39 | ["0", "0", "0", "0", "0"], 40 | ]) 41 | ); // 1 42 | 43 | console.log( 44 | numIslands([ 45 | ["1","1","0","0","0"], 46 | ["1","1","0","0","0"], 47 | ["0","0","1","0","0"], 48 | ["0","0","0","1","1"], 49 | ]) 50 | ); // 3 51 | 52 | // to see the output of this file use the command: npx ts-node src/13-graph/leetcode/number-of-islands.ts -------------------------------------------------------------------------------- /src/01-intro/08-typescript.js: -------------------------------------------------------------------------------- 1 | // Path: src/01-intro/08-typescript.ts 2 | let myName = 'Packt'; 3 | // myName = 10; // commented to avoid error 4 | /* Type inference */ 5 | let age = 20; // number 6 | let existsFlag = true; // boolean 7 | let language = 'JavaScript'; // string 8 | let favoriteLanguage; 9 | let langs = ['JavaScript', 'Ruby', 'Python']; 10 | favoriteLanguage = langs[0]; 11 | function printName(person) { 12 | console.log(person.name); 13 | } 14 | const john = { name: 'John', age: 21 }; 15 | const mary = { name: 'Mary', age: 21, phone: '123-45678' }; 16 | printName(john); 17 | printName(mary); 18 | class MyObject { 19 | age; 20 | constructor(age) { 21 | this.age = age; 22 | } 23 | compareTo(b) { 24 | if (this.age === b.age) { 25 | return 0; 26 | } 27 | return this.age > b.age ? 1 : -1; 28 | } 29 | } 30 | /* Enums */ 31 | var Compare; 32 | (function (Compare) { 33 | Compare[Compare["LESS_THAN"] = -1] = "LESS_THAN"; 34 | Compare[Compare["BIGGER_THAN"] = 1] = "BIGGER_THAN"; 35 | Compare[Compare["EQUALS"] = 0] = "EQUALS"; 36 | })(Compare || (Compare = {})); 37 | function compareTo(a, b) { 38 | if (a.age === b.age) { 39 | return Compare.EQUALS; 40 | } 41 | return a.age > b.age ? Compare.BIGGER_THAN : Compare.LESS_THAN; 42 | } 43 | -------------------------------------------------------------------------------- /src/10-tree/04-post-order-traversal.js: -------------------------------------------------------------------------------- 1 | // src/10-tree/04-post-order-traversal.js 2 | const BinarySearchTree = require('./binary-search-tree'); 3 | 4 | class FileOrDirectory { 5 | constructor(name, isDirectory, size = 0) { 6 | this.name = name; 7 | this.isDirectory = isDirectory; // true for directory, false for file 8 | } 9 | } 10 | const fileDirectoryComparator = (a, b) => a.name.localeCompare(b.name); 11 | 12 | const fileSystemTree = new BinarySearchTree(fileDirectoryComparator); 13 | fileSystemTree.insert(new FileOrDirectory('Project', true)); 14 | fileSystemTree.insert(new FileOrDirectory('Documents', true)); 15 | fileSystemTree.insert(new FileOrDirectory('Code', true)); 16 | fileSystemTree.insert(new FileOrDirectory('notes.txt', false)); 17 | fileSystemTree.insert(new FileOrDirectory('design.pdf', false)); 18 | fileSystemTree.insert(new FileOrDirectory('app.js', false)); 19 | 20 | 21 | // post order traversal 22 | const deleteFileOrDirectory = (fileDirectory) => { 23 | console.log(`Deleting ${fileDirectory.name}`); 24 | } 25 | fileSystemTree.postOrderTraverse(deleteFileOrDirectory); 26 | 27 | // Deleting app.js 28 | // Deleting design.pdf 29 | // Deleting Code 30 | // Deleting notes.txt 31 | // Deleting Documents 32 | // Deleting Project 33 | 34 | // to see the output of this file use the command: node src/10-tree/04-post-order-traversal.js -------------------------------------------------------------------------------- /src/03-array/hackerrank/01-arrays-ds.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/hackerrank/01-arrays-ds.ts 2 | // Problem: https://www.hackerrank.com/challenges/arrays-ds/problem 3 | 4 | // Solution 1 5 | function reverseArray(a: number[]): number[] { 6 | return a.reverse(); 7 | } 8 | 9 | // Solution 2 10 | function reverseArray2(a: number[]): number[] { 11 | const result = []; 12 | for (let i = a.length - 1; i >= 0; i--) { 13 | result.push(a[i]); 14 | } 15 | return result; 16 | } 17 | 18 | // Solution 3 19 | function reverseArray3(a: number[]): number[] { 20 | return a.map((_, i) => a[a.length - 1 - i]); 21 | } 22 | 23 | // Solution 4 24 | function reverseArray4(a: number[]): number[] { 25 | const result = []; 26 | for (let i = 0; i < a.length; i++) { 27 | result.unshift(a[i]); 28 | } 29 | return result; 30 | } 31 | 32 | // Solution 5, swap 33 | function reverseArray5(a: number[]): number[] { 34 | for (let i = 0; i < a.length / 2; i++) { 35 | const temp = a[i]; 36 | a[i] = a[a.length - 1 - i]; 37 | a[a.length - 1 - i] = temp; 38 | } 39 | return a; 40 | } 41 | 42 | // Test cases 43 | console.log(reverseArray([1, 4, 3, 2])); // [2, 3, 4, 1] 44 | console.log(reverseArray2([1, 4, 3, 2])); // [2, 3, 4, 1] 45 | console.log(reverseArray3([1, 4, 3, 2])); // [2, 3, 4, 1] 46 | console.log(reverseArray4([1, 4, 3, 2])); // [2, 3, 4, 1] 47 | 48 | -------------------------------------------------------------------------------- /src/02-bigOnotation/02-bigOChart.js: -------------------------------------------------------------------------------- 1 | google.load('visualization', '1.0', {'packages':['corechart']}); 2 | google.setOnLoadCallback(drawChart); 3 | 4 | function drawChart() { 5 | 6 | const data = new google.visualization.DataTable(); 7 | data.addColumn('string', 'n'); 8 | data.addColumn('number', 'O(1)'); 9 | data.addColumn('number', 'O(log n)'); 10 | data.addColumn('number', 'O(n)'); 11 | data.addColumn('number', 'O(n log n)'); 12 | data.addColumn('number', 'O(n^2)'); 13 | data.addColumn('number', 'O(2^n)'); 14 | //data.addColumn('number', 'O(n!)'); 15 | 16 | for (let i = 0; i <= 30; i++) { 17 | data.addRow([i+'', 1, Math.log(i), i, Math.log(i)*i, Math.pow(i,2), Math.pow(2,i)]); 18 | } 19 | 20 | const options = { 21 | 'width':700, 22 | 'height':600, 23 | 'backgroundColor':{stroke:'#CCC',strokeWidth:1}, 24 | 'curveType':'function', 25 | 'legend': { position: 'right'}, 26 | 'hAxis':{ 27 | title:'Elements (n)', 28 | showTextEvery:5 29 | }, 30 | 'vAxis':{ 31 | title:'Operations', 32 | viewWindowMode:'explicit', 33 | viewWindow:{min:0,max:300} 34 | } 35 | }; 36 | 37 | const chart = new google.visualization.LineChart(document.getElementById('chart_div')); 38 | chart.draw(data, options); 39 | } -------------------------------------------------------------------------------- /src/08-dictionary-hash/dictionary.js: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/dictionary.js 2 | 3 | class Dictionary { 4 | #items = {}; 5 | #size = 0; 6 | 7 | hasKey(key) { 8 | return this.#items[this.#elementToString(key)] != null; 9 | } 10 | 11 | set(key, value) { 12 | if (key != null && value != null) { 13 | const tableKey = this.#elementToString(key); 14 | this.#items[tableKey] = value; 15 | this.#size++; 16 | return true; 17 | } 18 | return false; 19 | } 20 | 21 | delete(key) { 22 | if (this.hasKey(key)) { 23 | delete this.#items[this.#elementToString(key)]; 24 | this.#size--; 25 | return true; 26 | } 27 | return false; 28 | } 29 | 30 | get(key) { 31 | return this.#items[this.#elementToString(key)]; 32 | } 33 | 34 | values() { 35 | return Object.values(this.#items); 36 | } 37 | 38 | keys() { 39 | return Object.keys(this.#items); 40 | } 41 | 42 | forEach(callbackFn) { 43 | for (const key in this.#items) { 44 | if (this.#items.hasOwnProperty(key)) { 45 | callbackFn(this.#items[key], key); 46 | } 47 | } 48 | } 49 | 50 | #elementToString(data) { 51 | if (typeof data === 'object' && data !== null) { 52 | return JSON.stringify(data); 53 | } else { 54 | return data.toString(); 55 | } 56 | } 57 | } 58 | 59 | module.exports = Dictionary; -------------------------------------------------------------------------------- /src/01-intro/07-modern.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Path: src/01-intro/07-modern.js 3 | 4 | /** 5 | * Arrow function example, calculates the area of a circle 6 | * @param {number} radius 7 | * @returns 8 | */ 9 | const circleAreaFn = function circleArea(radius) { 10 | const PI = 3.14; 11 | const area = PI * radius * radius; 12 | return area; 13 | }; 14 | // circleAreaFn('book'); 15 | console.log(circleAreaFn(2)); // 12.56 16 | 17 | // refactoring to use arrow function 18 | const circleArea = (radius) => { // {1} 19 | const PI = 3.14; 20 | return PI * radius * radius; 21 | }; 22 | 23 | // simplified version 24 | const circleAreaSimp = radius => 3.14 * radius * radius; 25 | console.log(circleAreaSimp(2)); // 12.56 26 | 27 | // no parameters 28 | const hello = () => console.log('hello!'); 29 | hello(); // hello! 30 | 31 | /* Spread operator example */ 32 | const sum = (x, y, z) => x + y + z; 33 | 34 | const numbers = [1, 2, 3]; 35 | console.log(sum(...numbers)); // 6 36 | 37 | /* Rest operator example */ 38 | const restParamaterFunction = (x, y, ...a) => (x + y) * a.length; 39 | console.log(restParamaterFunction(1, 2, 'hello', true, 7)); // 9 40 | 41 | /* Exponentiation operator example */ 42 | const r = 2; 43 | let area = 3.14 * r * r; 44 | area = 3.14 * Math.pow(r, 2); 45 | area = 3.14 * r ** 2; 46 | 47 | // to see the output of this file use the command: node src/01-intro/07-modern.js -------------------------------------------------------------------------------- /src/05-queue-deque/03-using-deque-class.js: -------------------------------------------------------------------------------- 1 | // src/05-queue-deque/03-using-deque-class.js 2 | 3 | const Deque = require('./deque'); 4 | 5 | class BrowserHistory { 6 | #history = new Deque(); // Stores visited pages 7 | #currentPage = null; 8 | 9 | visit(url) { 10 | this.#history.addFront(url); 11 | this.#currentPage = url; 12 | } 13 | 14 | goBack() { 15 | if (this.#history.size() > 1) { // Check if there's a previous page 16 | this.#history.removeFront(); // Remove the current page 17 | this.#currentPage = this.#history.peekFront(); // Set current to the previous 18 | } 19 | } 20 | 21 | goForward() { 22 | if (this.#currentPage !== this.#history.peekBack()) { // Check if there's a next page 23 | this.#history.addFront(this.#currentPage); // Add the current page back 24 | this.#currentPage = this.#history.removeFront(); // Set current to the next page 25 | } 26 | } 27 | 28 | get currentPage() { 29 | return this.#currentPage; 30 | } 31 | } 32 | 33 | const browser = new BrowserHistory(); 34 | browser.visit('loiane.com'); 35 | browser.visit('https://loiane.com/about'); // click on About menu 36 | 37 | browser.goBack(); 38 | console.log(browser.currentPage); // loiane.com 39 | 40 | browser.goForward(); 41 | console.log(browser.currentPage); // https://loiane.com/about 42 | 43 | 44 | // to see the output of this file use the command: node src/05-queue-deque/03-using-deque-class.js -------------------------------------------------------------------------------- /src/08-dictionary-hash/02-using-map-class.js: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/02-using-map-class.js 2 | 3 | const translations = new Map(); 4 | 5 | // Add some translations - English to Portuguese 6 | translations.set("hello", "olá"); 7 | translations.set("thank you", "obrigado"); 8 | translations.set("book", "livro"); 9 | translations.set("cat", "gato"); 10 | translations.set("computer", "computador"); 11 | 12 | // User interaction 13 | function translateWord(word) { 14 | if (translations.has(word)) { 15 | const translation = translations.get(word); 16 | console.log(`The translation of "${word}" is "${translation}"`); 17 | } else { 18 | console.log(`Sorry, no translation found for "${word}"`); 19 | } 20 | } 21 | 22 | // Example usage 23 | translateWord("hello"); // Output: The translation of "hello" is "olá" 24 | translateWord("dog"); // Output: Sorry, no translation found for "dog" 25 | 26 | // Get all translations 27 | console.log("All translations:", translations.values()); 28 | // All translations: [ 'olá', 'obrigado', 'livro', 'gato', 'computador' ] 29 | 30 | // Get all words 31 | console.log("All words:", translations.keys()); 32 | // All words: [ 'hello', 'thank you', 'book', 'cat', 'computer' ] 33 | 34 | // Iterate through all translations 35 | translations.forEach((value, key) => { 36 | console.log(`${key}: ${value}`); 37 | }); 38 | 39 | 40 | // to see the output of this file use the command: node src/08-dictionary-hash/01-using-dictionary-class.js -------------------------------------------------------------------------------- /src/10-tree/leetcode/binary-tree-postorder-traversal.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/binary-tree-postorder-traversal/description/ 2 | // 145. Binary Tree Postorder Traversal 3 | 4 | class TreeNode { 5 | val: number 6 | left: TreeNode | null 7 | right: TreeNode | null 8 | constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { 9 | this.val = (val === undefined ? 0 : val) 10 | this.left = (left === undefined ? null : left) 11 | this.right = (right === undefined ? null : right) 12 | } 13 | } 14 | 15 | function postorderTraversal(root: TreeNode | null): number[] { 16 | // Recursive 17 | //if (!root) return []; 18 | //return [...postorderTraversal(root.left), ...postorderTraversal(root.right), root.val]; 19 | 20 | // Iterative 21 | const stack: TreeNode[] = []; 22 | const result: number[] = []; 23 | let current = root; 24 | let lastVisited: TreeNode | null = null; 25 | while (stack.length !== 0 || current !== null) { 26 | if (current) { 27 | stack.push(current) 28 | current = current.left; 29 | } else { 30 | const peek = stack[stack.length - 1]; 31 | if (peek.right && peek.right !== lastVisited) { 32 | current = peek.right; 33 | } else { 34 | result.push(peek.val); 35 | lastVisited = stack.pop(); 36 | } 37 | } 38 | } 39 | return result; 40 | }; 41 | 42 | // Time complexity: O(n) 43 | // Space complexity: O(n) (worst case) - O(log n) (average case) -------------------------------------------------------------------------------- /src/10-tree/02-in-order-traversal.js: -------------------------------------------------------------------------------- 1 | // src/10-tree/02-in-order-traversal.js 2 | const BinarySearchTree = require('./binary-search-tree'); 3 | 4 | class Student { 5 | constructor(idNumber, firstName, lastName) { 6 | this.idNumber = idNumber; 7 | this.lastName = lastName; 8 | this.firstName = firstName; 9 | } 10 | } 11 | 12 | // create student comparator to compare lastName using localCompare 13 | const studentComparator = (a, b) => a.lastName.localeCompare(b.lastName); 14 | 15 | const studentTree = new BinarySearchTree(studentComparator); 16 | 17 | studentTree.insert(new Student(9, 'Sofia', 'Cygnus')); 18 | studentTree.insert(new Student(12, 'Seth', 'Capella')); 19 | studentTree.insert(new Student(11, 'Darcy', 'Vega')); 20 | studentTree.insert(new Student(7, 'Tory', 'Vega')); 21 | studentTree.insert(new Student(5, 'Caleb', 'Altair')); 22 | studentTree.insert(new Student(15, 'Max', 'Rigel')); 23 | 24 | 25 | // in order traversal 26 | const classRoster = []; 27 | const addToRoster = (studentData) => { 28 | classRoster.push(`${studentData.idNumber}: ${studentData.lastName}, ${studentData.firstName}`); 29 | } 30 | studentTree.inOrderTraverse(addToRoster); 31 | 32 | console.log(classRoster); 33 | // [ 34 | // '5: Altair, Caleb', 35 | // '12: Capella, Seth', 36 | // '9: Cygnus, Sofia', 37 | // '15: Rigel, Max', 38 | // '11: Vega, Darcy', 39 | // '7: Vega, Tory' 40 | // ] 41 | 42 | // to see the output of this file use the command: node src/10-tree/02-in-order-traversal.js -------------------------------------------------------------------------------- /src/03-array/03-iterator-functions.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/03-iterator-functions.ts 2 | 3 | // @ts-ignore 4 | const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 5 | 6 | // using forEach method 7 | numbers.forEach((value, index) => { 8 | console.log(`numbers[${index}]`, value); 9 | }); 10 | 11 | numbers.forEach(value => console.log(value)); 12 | 13 | // using every method 14 | const isBelowSeven = numbers.every(value => value < 7); 15 | console.log('All values are below 7?:', isBelowSeven); // false 16 | 17 | // rewriting the preceding code using for loop 18 | let isBelowSevenForLoop = true; 19 | for (let i = 0; i < numbers.length; i++) { 20 | if (numbers[i] >= 7) { 21 | isBelowSevenForLoop = false; 22 | break; 23 | } 24 | } 25 | console.log('All values are below 7?:', isBelowSevenForLoop); // false 26 | 27 | // using some method 28 | const isSomeValueBelowSeven = numbers.some(value => value < 7); 29 | console.log('Is any value below 7?:', isSomeValueBelowSeven); // true 30 | 31 | // rewriting the preceding code using for loop 32 | let isSomeValueBelowSevenForLoop = false; 33 | for (let i = 0; i < numbers.length; i++) { 34 | if (numbers[i] < 7) { 35 | isSomeValueBelowSevenForLoop = true; 36 | break; 37 | } 38 | } 39 | 40 | // using filter method 41 | // @ts-ignore 42 | const valuesBelowSeven = numbers.filter(value => value < 7); 43 | console.log('Values below 7:', valuesBelowSeven); // [1, 2, 3, 4, 5, 6] 44 | 45 | // to see the output of this file use the command: node src/03-array/03-iterator-functions.ts -------------------------------------------------------------------------------- /src/12-trie/01-spell-checker.js: -------------------------------------------------------------------------------- 1 | // src/12-trie/01-spell-checker.js 2 | 3 | const Trie = require('./trie'); 4 | 5 | class SpellChecker { 6 | #trie = new Trie(); 7 | 8 | buildDictionary(words) { 9 | for (let word of words) { 10 | this.#trie.insert(word); 11 | } 12 | } 13 | 14 | isWordInDictionary(word) { 15 | return this.#trie.search(word); 16 | } 17 | 18 | getSuggestions(word) { 19 | const suggestions = []; 20 | const wordArray = word.split(''); 21 | for (let i = 0; i < wordArray.length; i++) { 22 | const temp = wordArray[i]; 23 | wordArray[i] = ''; 24 | const newWord = wordArray.join(''); 25 | if (this.#trie.startsWith(newWord)) { 26 | suggestions.push(newWord); 27 | } 28 | wordArray[i] = temp; 29 | } 30 | return suggestions; 31 | } 32 | 33 | removeWord(word) { 34 | return this.#trie.remove(word); 35 | } 36 | } 37 | 38 | const spellChecker = new SpellChecker(); 39 | spellChecker.buildDictionary(['cat', 'bat', 'rat', 'drat', 'dart', 'date']); 40 | console.log(spellChecker.isWordInDictionary('bat')); // true 41 | console.log(spellChecker.isWordInDictionary('drate')); // false 42 | console.log(spellChecker.getSuggestions('drate')); // [ 'date', 'drat' ] 43 | 44 | 45 | const trie = new Trie(); 46 | trie.insert('she'); 47 | trie.insert('sells'); 48 | trie.insert('sea'); 49 | trie.insert('shells'); 50 | trie.insert('by'); 51 | trie.insert('the'); 52 | 53 | // to see the output of this file use the command: node src/12-trie/01-spell-checker.js -------------------------------------------------------------------------------- /src/08-dictionary-hash/01-using-dictionary-class.js: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/01-using-dictionary-class.js 2 | 3 | const Dictionary = require('./dictionary'); 4 | 5 | const translations = new Dictionary(); 6 | 7 | // Add some translations - English to Portuguese 8 | translations.set("hello", "olá"); 9 | translations.set("thank you", "obrigado"); 10 | translations.set("book", "livro"); 11 | translations.set("cat", "gato"); 12 | translations.set("computer", "computador"); 13 | 14 | // User interaction 15 | function translateWord(word) { 16 | if (translations.hasKey(word)) { 17 | const translation = translations.get(word); 18 | console.log(`The translation of "${word}" is "${translation}"`); 19 | } else { 20 | console.log(`Sorry, no translation found for "${word}"`); 21 | } 22 | } 23 | 24 | // Example usage 25 | translateWord("hello"); // Output: The translation of "hello" is "olá" 26 | translateWord("dog"); // Output: Sorry, no translation found for "dog" 27 | 28 | // Get all translations 29 | console.log("All translations:", translations.values()); 30 | // All translations: [ 'olá', 'obrigado', 'livro', 'gato', 'computador' ] 31 | 32 | // Get all words 33 | console.log("All words:", translations.keys()); 34 | // All words: [ 'hello', 'thank you', 'book', 'cat', 'computer' ] 35 | 36 | // Iterate through all translations 37 | translations.forEach((value, key) => { 38 | console.log(`${key}: ${value}`); 39 | }); 40 | 41 | 42 | // to see the output of this file use the command: node src/08-dictionary-hash/01-using-dictionary-class.js -------------------------------------------------------------------------------- /src/13-graph/prim.js: -------------------------------------------------------------------------------- 1 | const INF = Number.MAX_SAFE_INTEGER; 2 | 3 | const minKey = (graph, key, visited) => { 4 | let min = INF; 5 | let minIndex = 0; 6 | for (let v = 0; v < graph.length; v++) { 7 | if (visited[v] === false && key[v] < min) { 8 | min = key[v]; 9 | minIndex = v; 10 | } 11 | } 12 | return minIndex; 13 | }; 14 | 15 | const prim = (graph) => { 16 | const parent = []; // Stores the MST 17 | const key = []; // Keeps track of the minimum edge weights 18 | const visited = []; // Marks visited vertices 19 | const { length } = graph; 20 | 21 | // Initialize all key values as infinite and visited as false 22 | for (let i = 0; i < length; i++) { 23 | key[i] = INF; 24 | visited[i] = false; 25 | } 26 | 27 | key[0] = 0; // Start with the first vertex 28 | parent[0] = -1; // The first vertex is the root of the MST 29 | 30 | // Find the MST for all vertices 31 | for (let i = 0; i < length - 1; i++) { 32 | const u = minKey(graph, key, visited); // Select the vertex with the minimum key value 33 | visited[u] = true; // Mark the selected vertex as visited 34 | 35 | // Update key values and parent for adjacent vertices 36 | for (let v = 0; v < length; v++) { 37 | if (graph[u][v] && !visited[v] && graph[u][v] < key[v]) { 38 | parent[v] = u; // Update parent to store the edge in the MST 39 | key[v] = graph[u][v]; // Update key value to the minimum edge weight 40 | } 41 | } 42 | } 43 | 44 | return parent; // Return the MST 45 | }; 46 | 47 | module.exports = prim; -------------------------------------------------------------------------------- /src/05-queue-deque/deque.ts: -------------------------------------------------------------------------------- 1 | // src/05-queue-deque/deque.ts 2 | 3 | class Deque { 4 | private items: T[] = []; 5 | 6 | addFront(item: T): void { 7 | this.items.unshift(item); 8 | } 9 | 10 | addRear(item: T): void { 11 | this.items.push(item); 12 | } 13 | 14 | removeFront(): T | undefined { 15 | if (this.isEmpty()) { 16 | return undefined; 17 | } 18 | return this.items.shift(); 19 | } 20 | 21 | removeRear(): T | undefined { 22 | if (this.isEmpty()) { 23 | return undefined; 24 | } 25 | return this.items.pop(); 26 | } 27 | 28 | peekFront(): T | undefined { 29 | if (this.isEmpty()) { 30 | return undefined; 31 | } 32 | return this.items[0]; 33 | } 34 | 35 | peekRear(): T | undefined { 36 | if (this.isEmpty()) { 37 | return undefined; 38 | } 39 | return this.items[this.items.length - 1]; 40 | } 41 | 42 | isEmpty(): boolean { 43 | return this.items.length === 0; 44 | } 45 | 46 | get size(): number { 47 | return this.items.length; 48 | } 49 | 50 | clear(): void { 51 | this.items = []; 52 | } 53 | 54 | toString(): string { 55 | if (this.isEmpty()) { 56 | return 'Empty Deque'; 57 | } else { 58 | return this.items.map(item => { // {1} 59 | if (typeof item === 'object' && item !== null) { // {2} 60 | return JSON.stringify(item); // Handle objects 61 | } else { 62 | return item.toString(); // Handle other types {3} 63 | } 64 | }).join(', '); // {4} 65 | } 66 | } 67 | } 68 | 69 | export default Deque; -------------------------------------------------------------------------------- /src/13-graph/dijkstra.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/dijkstra.js 2 | 3 | const INF = Number.MAX_SAFE_INTEGER; // Infinity 4 | 5 | const dijkstra = (graph, src) => { 6 | const dist = []; 7 | const visited = []; 8 | const pred = []; // Predecessor array 9 | const { length } = graph; 10 | for (let i = 0; i < length; i++) { 11 | dist[i] = INF; 12 | visited[i] = false; 13 | pred[i] = null; // Initialize predecessors 14 | } 15 | dist[src] = 0; 16 | for (let i = 0; i < length - 1; i++) { 17 | const unv = minDistance(dist, visited); 18 | visited[unv] = true; 19 | for (let v = 0; v < length; v++) { 20 | if (!visited[v] && graph[unv][v] !== 0 && dist[unv] !== INF && dist[unv] + graph[unv][v] < dist[v]) { 21 | dist[v] = dist[unv] + graph[unv][v]; 22 | pred[v] = unv; // Update predecessor 23 | } 24 | } 25 | } 26 | 27 | // Construct paths from predecessors 28 | const paths = {}; 29 | for (let i = 0; i < length; i++) { 30 | paths[i] = []; 31 | let crawl = i; 32 | paths[i].push(crawl); 33 | while (pred[crawl] !== null) { 34 | paths[i].push(pred[crawl]); 35 | crawl = pred[crawl]; 36 | } 37 | paths[i].reverse(); 38 | } 39 | return { distances: dist, predecessors: paths }; 40 | }; 41 | 42 | const minDistance = (dist, visited) => { 43 | let min = INF; 44 | let minIndex = -1; 45 | for (let v = 0; v < dist.length; v++) { 46 | if (visited[v] === false && dist[v] <= min) { 47 | min = dist[v]; 48 | minIndex = v; 49 | } 50 | } 51 | return minIndex; 52 | }; 53 | 54 | module.exports = dijkstra; -------------------------------------------------------------------------------- /src/12-trie/leetcode/implement-trie-prefix-tree.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/implement-trie-prefix-tree/ 2 | // 208. Implement Trie (Prefix Tree) 3 | 4 | 5 | class TrieNode { 6 | children: Map; 7 | isEndOfWord: boolean; 8 | 9 | constructor() { 10 | this.children = new Map(); 11 | this.isEndOfWord = false; 12 | } 13 | } 14 | 15 | class Trie { 16 | private root: TrieNode; 17 | 18 | constructor() { 19 | this.root = new TrieNode(); 20 | } 21 | 22 | insert(word: string): void { 23 | let node = this.root; 24 | for (let char of word) { 25 | if (!node.children.has(char)) { 26 | node.children.set(char, new TrieNode()); 27 | } 28 | node = node.children.get(char)!; 29 | } 30 | node.isEndOfWord = true; 31 | } 32 | 33 | search(word: string): boolean { 34 | let node = this.root; 35 | for (let char of word) { 36 | if (!node.children.has(char)) { 37 | return false; 38 | } 39 | node = node.children.get(char)!; 40 | } 41 | return node.isEndOfWord; 42 | } 43 | 44 | startsWith(prefix: string): boolean { 45 | let node = this.root; 46 | for (let char of prefix) { 47 | if (!node.children.has(char)) { 48 | return false; 49 | } 50 | node = node.children.get(char)!; 51 | } 52 | return true; 53 | } 54 | } 55 | 56 | /** 57 | * Your Trie object will be instantiated and called as such: 58 | * var obj = new Trie() 59 | * obj.insert(word) 60 | * var param_2 = obj.search(word) 61 | * var param_3 = obj.startsWith(prefix) 62 | */ -------------------------------------------------------------------------------- /src/01-intro/08-typescript.ts: -------------------------------------------------------------------------------- 1 | // Path: src/01-intro/08-typescript.ts 2 | 3 | let myName = 'Packt'; 4 | // myName = 10; // commented to avoid error 5 | 6 | /* Type inference */ 7 | let age = 20; // number 8 | let existsFlag = true; // boolean 9 | let language = 'JavaScript'; // string 10 | 11 | let favoriteLanguage: string; 12 | let langs = ['JavaScript', 'Ruby', 'Python']; 13 | favoriteLanguage = langs[0]; 14 | 15 | /* Interfaces as type */ 16 | interface Friend { 17 | name: string; 18 | age: number; 19 | } 20 | 21 | function printName(person: Friend) { 22 | console.log(person.name); 23 | } 24 | 25 | const john = { name: 'John', age: 21 }; 26 | const mary = { name: 'Mary', age: 21, phone: '123-45678' }; 27 | printName(john); 28 | printName(mary); 29 | 30 | /* Interfaces as OO */ 31 | interface Comparable { 32 | compareTo(b: T): number; 33 | } 34 | 35 | class MyObject implements Comparable { 36 | age: number; 37 | 38 | constructor(age: number) { 39 | this.age = age; 40 | } 41 | 42 | compareTo(b: MyObject): number { 43 | if (this.age === b.age) { 44 | return 0; 45 | } 46 | return this.age > b.age ? 1 : -1; 47 | } 48 | } 49 | 50 | /* Enums */ 51 | enum Compare { 52 | LESS_THAN = -1, 53 | BIGGER_THAN = 1, 54 | EQUALS = 0 55 | } 56 | 57 | function compareTo(a: MyObject, b: MyObject): number { 58 | if (a.age === b.age) { 59 | return Compare.EQUALS; 60 | } 61 | return a.age > b.age ? Compare.BIGGER_THAN : Compare.LESS_THAN; 62 | } 63 | 64 | /* Type aliases */ 65 | type UserID = string; 66 | type User = { 67 | id: UserID; 68 | name: string; 69 | } -------------------------------------------------------------------------------- /src/13-graph/leetcode/course-schedule.ts: -------------------------------------------------------------------------------- 1 | // 207. Course Schedule 2 | // https://leetcode.com/problems/course-schedule/ 3 | 4 | // Time complexity: O(V + E) 5 | // Space complexity: O(V + E) 6 | 7 | function canFinish(numCourses: number, prerequisites: number[][]): boolean { 8 | const adjList = new Map(); 9 | const visited = new Set(); 10 | 11 | // Create adjacency list 12 | for (let i = 0; i < numCourses; i++) { 13 | adjList.set(i, []); 14 | } 15 | for (const [course, pre] of prerequisites) { 16 | adjList.get(pre).push(course); 17 | } 18 | 19 | // DFS function to check for cycles 20 | const dfs = (course, cycle) => { 21 | if (cycle.has(course)) { 22 | return true; 23 | } 24 | if (visited.has(course)) { 25 | return false; 26 | } 27 | cycle.add(course); 28 | visited.add(course); 29 | for (const neighbor of adjList.get(course)) { 30 | if (dfs(neighbor, cycle)) { 31 | return true; 32 | } 33 | } 34 | cycle.delete(course); 35 | return false; 36 | } 37 | 38 | // Check for cycles starting from each course 39 | for (let i = 0; i < numCourses; i++) { 40 | if (dfs(i, new Set())) { 41 | return false; // Cycle found 42 | } 43 | } 44 | 45 | return true; // No cycles found 46 | } 47 | 48 | // Test cases 49 | const numCourses = 2; 50 | const prerequisites = [ 51 | [1, 0], 52 | [0, 1], 53 | ]; 54 | console.log(canFinish(numCourses, prerequisites)); // false 55 | 56 | // to see the output of this file use the command: npx ts-node src/13-graph/leetcode/course-schedule.ts -------------------------------------------------------------------------------- /src/09-recursion/04-fibonacci.js: -------------------------------------------------------------------------------- 1 | // src/09-recursion/04-fibonacci.js 2 | 3 | // iterative approach 4 | function fibonacciIterative(n) { 5 | if (n < 0) { 6 | throw new Error('Input must be a non-negative integer'); 7 | } 8 | if (n < 2) { return n; } 9 | 10 | let prevPrev = 0; 11 | let prev = 1; 12 | let current; 13 | 14 | for (let i = 2; i <= n; i++) { // n >= 2 15 | current = prev + prevPrev; // f(n-1) + f(n-2) 16 | prevPrev = prev; 17 | prev = current; 18 | } 19 | 20 | return current; 21 | } 22 | 23 | console.log('fibonacciIterative(2)', fibonacciIterative(2)); // 1 24 | console.log('fibonacciIterative(3)', fibonacciIterative(3)); // 2 25 | console.log('fibonacciIterative(4)', fibonacciIterative(4)); // 3 26 | console.log('fibonacciIterative(5)', fibonacciIterative(5)); // 5 27 | 28 | // recursive approach 29 | function fibonacci(n) { 30 | if (n < 0) { 31 | throw new Error('Input must be a non-negative integer'); 32 | } 33 | if (n < 2) { return n; } // base case 34 | return fibonacci(n - 1) + fibonacci(n - 2); // recursive case 35 | } 36 | 37 | console.log('fibonacci(5)', fibonacci(5)); // 5 38 | 39 | // memoization approach 40 | function fibonacciMemoization(n) { 41 | if (n < 0) { 42 | throw new Error('Input must be a non-negative integer'); 43 | } 44 | const memo = [0, 1]; 45 | const fibonacci = (n) => { 46 | if (memo[n] != null) return memo[n]; 47 | return memo[n] = fibonacci(n - 1) + fibonacci(n - 2); 48 | }; 49 | return fibonacci(n); 50 | } 51 | 52 | console.log('fibonacciMemoization(5)', fibonacciMemoization(5)); // 5 53 | 54 | // to see the output of this file use the command: node src/09-recursion/04-fibonacci.js -------------------------------------------------------------------------------- /src/08-dictionary-hash/hash-table.ts: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/hash-table.ts 2 | 3 | class HashTable { 4 | 5 | private table: V[] = []; 6 | 7 | #loseLoseHashCode(key: string) { 8 | // if (typeof key !== 'string') { 9 | // key = this.#elementToString(key); 10 | // } 11 | const calcASCIIValue = (acc, char) => acc + char.charCodeAt(0); 12 | const hash = key.split('').reduce(calcASCIIValue, 0); 13 | return hash % 37; 14 | } 15 | 16 | hash(key: string) { 17 | return this.#loseLoseHashCode(key); 18 | } 19 | 20 | put(key: string, value: V) { 21 | const index = this.hash(key); 22 | this.table[index] = value; 23 | return true; 24 | } 25 | 26 | get(key: string): V { 27 | const index = this.hash(key); 28 | return this.table[index]; 29 | } 30 | 31 | remove(key: string) { 32 | if (key == null) { 33 | return false; 34 | } 35 | const index = this.hash(key); 36 | if (this.table[index]) { 37 | delete this.table[index]; 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | #elementToString(data: V) { 44 | if (typeof data === 'object' && data !== null) { 45 | return JSON.stringify(data); 46 | } else { 47 | return data.toString(); 48 | } 49 | } 50 | 51 | toString() { 52 | const keys = Object.keys(this.table); 53 | let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; 54 | for (let i = 1; i < keys.length; i++) { 55 | const value = this.#elementToString(this.table[keys[i]]).toString(); 56 | objString = `${objString}\n{${keys[i]} => ${value}}`; 57 | } 58 | return objString; 59 | } 60 | } 61 | 62 | export default HashTable; -------------------------------------------------------------------------------- /src/01-intro/01-hello-variables.js: -------------------------------------------------------------------------------- 1 | // Path: src/01-intro/01-hello-variables.js 2 | 3 | // hello world 4 | console.log('Hello, World!'); 5 | 6 | // variables 7 | var num = 1; 8 | num = 'one' ; 9 | 10 | let myVar = 2; 11 | myVar = 4; 12 | 13 | const price = 1.5; // number 14 | const publisher = 'Packt'; // string 15 | const javaScriptBook = true; // boolean 16 | const nullVar = null; // null 17 | let und; // undefined 18 | 19 | console.log('price: ' + price); 20 | console.log('publisher: ' + publisher); 21 | console.log('javaScriptBook: ' + javaScriptBook); 22 | console.log('nullVar: ' + nullVar); 23 | console.log('und: ' + und); 24 | 25 | console.log('**** Data types ****'); 26 | 27 | console.log('typeof price: ', typeof price); // number 28 | console.log('typeof publisher: ', typeof publisher); // string 29 | console.log('typeof javaScriptBook: ', typeof javaScriptBook); // boolean 30 | console.log('typeof nullVar: ', typeof nullVar); // object 31 | console.log('typeof und: ', typeof und); // undefined 32 | 33 | const book = { 34 | title: 'Data Structures and Algorithms', 35 | } 36 | 37 | console.log('book title: ', book.title); 38 | 39 | book.title = 'Data Structures and Algorithms in JavaScript'; 40 | // book = { anotherTitle: 'Data Structures’ } // this will not work 41 | 42 | // reassignment of objects: 43 | let book2 = { 44 | title: 'Data Structures and Algorithms', 45 | } 46 | book2 = { title: 'Data Structures' }; 47 | 48 | // symbol 49 | const title = Symbol('title'); 50 | const book3 = { 51 | [title]: 'Data Structures and Algorithms' 52 | }; 53 | console.log(book3[title]); // Data Structures and Algorithms 54 | 55 | // to see the output of this file use the command: node src/01-intro/01-hello-variables.js -------------------------------------------------------------------------------- /src/04-stack/leetcode/simplify-path.ts: -------------------------------------------------------------------------------- 1 | // Path: src/04-stack/leetcode/simplify-path.ts 2 | // https://leetcode.com/problems/simplify-path/description/ 3 | 4 | /** 5 | * @param {string} path 6 | * @return {string} 7 | */ 8 | const simplifyPath = function(path) { 9 | const stack = []; 10 | const paths = path.split('/'); 11 | 12 | for (let i = 0; i < paths.length; i++) { 13 | if (paths[i] === '' || paths[i] === '.') { 14 | continue; 15 | } else if (paths[i] === '..') { 16 | stack.pop(); 17 | } else { 18 | stack.push(paths[i]); 19 | } 20 | } 21 | return '/' + stack.join('/'); 22 | } 23 | 24 | // test 25 | console.log(simplifyPath('/home/')); // '/home' 26 | console.log(simplifyPath('/../')); // '/' 27 | console.log(simplifyPath('/home//foo/')); // '/home/foo' 28 | console.log(simplifyPath('/a/./b/../../c/')); // '/c' 29 | console.log(simplifyPath('/a/../../b/../c//.//')); // '/c' 30 | console.log(simplifyPath('/a//b////c/d//././/..')); // '/a/b/c' 31 | 32 | // additional tests 33 | console.log(simplifyPath('/home/user/Documents/../Pictures')); // '/home/user/Pictures' 34 | console.log(simplifyPath('/../home/user/Documents')); // '/home/user/Documents' 35 | console.log(simplifyPath('/home/user/../../usr/local/bin')); // '/usr/local/bin' 36 | console.log(simplifyPath('/home/user/./Downloads/../Pictures/././')); // '/home/user/Pictures' 37 | console.log(simplifyPath('/home/user/Documents/../../usr/local/bin')); // '/usr/local/bin' 38 | console.log(simplifyPath('/home/user/Documents/../../../usr/local/bin')); // '/usr/local/bin' 39 | 40 | // time complexity: O(n) 41 | // space complexity: O(n) 42 | 43 | // to see the output of this file use the command: node src/04-stack/leetcode/simplify-path.ts -------------------------------------------------------------------------------- /src/03-array/05-transforming-array.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/05-transforming-array.ts 2 | 3 | // @ts-ignore 4 | const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 5 | 6 | // using the map method 7 | const squaredNumbers = numbers.map(value => value * value); 8 | console.log('Squared numbers:', squaredNumbers); // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 9 | 10 | // rewriting the above code using a loop 11 | const squaredNumbersLoop = []; 12 | for (let i = 0; i < numbers.length; i++) { 13 | squaredNumbersLoop.push(numbers[i] * numbers[i]); 14 | } 15 | 16 | // using the split method 17 | const namesFromCSV = 'Aelin,Gandalf,Violet,Poppy'; 18 | // @ts-ignore 19 | const names = namesFromCSV.split(','); 20 | console.log('Names:', names); // ['Aelin', 'Gandalf', 'Violet', 'Poppy'] 21 | 22 | // using the join method 23 | const namesCSV = names.join(';'); 24 | console.log('Names CSV:', namesCSV); // 'Aelin;Gandalf;Violet;Poppy' 25 | 26 | // using the reduce method 27 | // @ts-ignore 28 | const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 29 | const sum = numbers.reduce((acc, value) => acc + value, 0); // 55 30 | 31 | // rewriting the above code using a loop 32 | let sumLoop = 0; 33 | for (let i = 0; i < numbers.length; i++) { 34 | sumLoop += numbers[i]; 35 | } 36 | 37 | // using the reduce method to find the maximum value 38 | const scores = [30, 70, 85, 90, 100]; 39 | const highestScore = scores.reduce((max, score) => score > max ? score : max, scores[0]); // 100 40 | 41 | // using reduceRight method 42 | const reversedNumbers = numbers.reduceRight((acc, value) => { 43 | acc.push(value); 44 | return acc; 45 | }, []); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] 46 | 47 | // to see the output of this file use the command: node src/03-array/05-transforming-array.ts -------------------------------------------------------------------------------- /src/12-trie/trie.js: -------------------------------------------------------------------------------- 1 | class TrieNode { 2 | constructor() { 3 | this.children = new Map(); 4 | this.isEndOfWord = false; 5 | } 6 | } 7 | 8 | class Trie { 9 | #root = new TrieNode(); 10 | 11 | insert(word) { 12 | let node = this.#root; 13 | for (let char of word) { 14 | if (!node.children.has(char)) { 15 | node.children.set(char, new TrieNode()); 16 | } 17 | node = node.children.get(char); 18 | } 19 | node.isEndOfWord = true; 20 | } 21 | 22 | search(word) { 23 | let node = this.#root; 24 | for (let char of word) { 25 | if (!node.children.has(char)) { 26 | return false; 27 | } 28 | node = node.children.get(char); 29 | } 30 | return node.isEndOfWord; 31 | } 32 | 33 | startsWith(prefix) { 34 | let node = this.#root; 35 | for (let char of prefix) { 36 | if (!node.children.has(char)) { 37 | return false; 38 | } 39 | node = node.children.get(char); 40 | } 41 | return true; 42 | } 43 | 44 | remove(word) { 45 | return this.#removeWord(this.#root, word, 0); 46 | } 47 | 48 | #removeWord(node, word, index) { 49 | if (index === word.length) { 50 | if (!node.isEndOfWord) return false; 51 | node.isEndOfWord = false; 52 | return node.children.size === 0; 53 | } 54 | 55 | const char = word[index]; 56 | if (!node.children.has(char)) { 57 | return false; 58 | } 59 | 60 | const shouldDeleteCurrentNode = this.#removeWord(node.children.get(char), word, index + 1); 61 | if (shouldDeleteCurrentNode) { 62 | node.children.delete(char); 63 | return node.children.size === 0; 64 | } 65 | 66 | return false; 67 | } 68 | } 69 | 70 | module.exports = Trie; -------------------------------------------------------------------------------- /src/08-dictionary-hash/hash-table.js: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/hash-table.js 2 | 3 | class HashTable { 4 | 5 | #table = []; 6 | 7 | #loseLoseHashCode(key) { 8 | if (typeof key !== 'string') { 9 | key = this.#elementToString(key); 10 | } 11 | const hash = key.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); 12 | return hash % 37; // mod to reduce the hash code 13 | } 14 | 15 | hash(key) { 16 | return this.#loseLoseHashCode(key); 17 | } 18 | 19 | put(key, value) { 20 | if (key == null && value == null) { 21 | return false; 22 | } 23 | const index = this.hash(key); 24 | this.#table[index] = value; 25 | return true; 26 | } 27 | 28 | get(key) { 29 | if (key == null) { 30 | return undefined; 31 | } 32 | const index = this.hash(key); 33 | return this.#table[index]; 34 | } 35 | 36 | remove(key) { 37 | if (key == null) { 38 | return false; 39 | } 40 | const index = this.hash(key); 41 | if (this.#table[index]) { 42 | delete this.#table[index]; 43 | return true; 44 | } 45 | return false; 46 | } 47 | 48 | #elementToString(data) { 49 | if (typeof data === 'object' && data !== null) { 50 | return JSON.stringify(data); 51 | } else { 52 | return data.toString(); 53 | } 54 | } 55 | 56 | toString() { 57 | const keys = Object.keys(this.#table); 58 | let objString = `{${keys[0]} => ${this.#table[keys[0]].toString()}}`; 59 | for (let i = 1; i < keys.length; i++) { 60 | const value = this.#elementToString(this.#table[keys[i]]).toString(); 61 | objString = `${objString}\n{${keys[i]} => ${value}}`; 62 | } 63 | return objString; 64 | } 65 | } 66 | 67 | module.exports = HashTable; -------------------------------------------------------------------------------- /src/12-trie/leetcode/design-add-and-search-words-data-structure.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/design-add-and-search-words-data-structure/description/ 2 | // 211. Design Add and Search Words Data Structure 3 | 4 | // @ts-ignore 5 | class TrieNode { 6 | children: Map; 7 | isEndOfWord: boolean; 8 | 9 | constructor() { 10 | this.children = new Map(); 11 | this.isEndOfWord = false; 12 | } 13 | } 14 | 15 | class WordDictionary { 16 | private root: TrieNode; 17 | 18 | constructor() { 19 | this.root = new TrieNode(); 20 | } 21 | 22 | addWord(word: string): void { 23 | let node = this.root; 24 | for (let char of word) { 25 | if (!node.children.has(char)) { 26 | node.children.set(char, new TrieNode()); 27 | } 28 | node = node.children.get(char)!; 29 | } 30 | node.isEndOfWord = true; 31 | } 32 | 33 | search(word: string): boolean { 34 | return this.searchFromNode(word, this.root); 35 | } 36 | 37 | private searchFromNode(word: string, node: TrieNode): boolean { 38 | for (let i = 0; i < word.length; i++) { 39 | const char = word[i]; 40 | if (char === '.') { 41 | for (let child of node.children.values()) { 42 | if (this.searchFromNode(word.slice(i + 1), child)) { 43 | return true; 44 | } 45 | } 46 | return false; 47 | } else if (!node.children.has(char)) { 48 | return false; 49 | } 50 | node = node.children.get(char)!; 51 | } 52 | return node.isEndOfWord; 53 | } 54 | } -------------------------------------------------------------------------------- /src/11-heap/leetcode/minimum-number-game.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/minimum-number-game/description/ 2 | // 2974. Minimum Number Game 3 | 4 | function siftDown (array: number[], heapSize: number, rootIndex: number) { 5 | let largest = rootIndex; 6 | let left = 2 * rootIndex + 1; 7 | let right = 2 * rootIndex + 2; 8 | 9 | if (left < heapSize && array[left] > array[largest]) { 10 | largest = left; 11 | } 12 | 13 | if (right < heapSize && array[right] > array[largest]) { 14 | largest = right; 15 | } 16 | 17 | if (largest !== rootIndex) { 18 | [array[rootIndex], array[largest]] = [array[largest], array[rootIndex]]; 19 | siftDown(array, heapSize, largest); 20 | } 21 | }; 22 | 23 | 24 | function buildMaxHeap (array: number[]) { 25 | const heapSize = array.length; 26 | for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) { 27 | siftDown(array, heapSize, i); 28 | } 29 | } 30 | 31 | function heapSort(array: number[]) { 32 | buildMaxHeap(array); 33 | for (let i = array.length - 1; i > 0; i--) { 34 | [array[0], array[i]] = [array[i], array[0]]; 35 | siftDown(array, i, 0); 36 | } 37 | } 38 | 39 | function numberGame(nums: number[]): number[] { 40 | // nums.sort((a, b) => a - b); 41 | heapSort(nums); 42 | const arr: number[] = []; 43 | while (nums.length > 0) { 44 | const alice = nums.splice(0, 1)[0]; 45 | const bob = nums.splice(0, 1)[0]; 46 | arr.push(bob); 47 | arr.push(alice); 48 | } 49 | return arr; 50 | }; 51 | 52 | console.log(numberGame([5,4,2,3])); // [ 3, 2, 5, 4 ] 53 | console.log(numberGame([2,5])); // [ 5, 2 ] 54 | 55 | // Time complexity: O(nlogn) 56 | // Space complexity: O(n) 57 | 58 | // to see the output of this file use the command: npx ts-node src/11-heap/leetcode/minimum-number-game.ts -------------------------------------------------------------------------------- /src/11-heap/heap-sort.js: -------------------------------------------------------------------------------- 1 | // src/11-heap/heap-sort.js 2 | 3 | const Comparator = require('../10-tree/comparator'); 4 | 5 | /** 6 | * Heapify the array. 7 | * @param {*} array to be heapified. 8 | * @param {*} heapSize size of the heap. 9 | * @param {*} rootIndex index of the root. 10 | * @param {*} compareFn compare function. 11 | */ 12 | const siftDown = (array, heapSize, rootIndex, compareFn) => { 13 | let largest = rootIndex; 14 | let left = 2 * rootIndex + 1; 15 | let right = 2 * rootIndex + 2; 16 | 17 | if (left < heapSize && compareFn.greaterThan(array[left], array[largest])) { 18 | largest = left; 19 | } 20 | 21 | if (right < heapSize && compareFn.greaterThan(array[right], array[largest])) { 22 | largest = right; 23 | } 24 | 25 | if (largest !== rootIndex) { 26 | [array[rootIndex], array[largest]] = [array[largest], array[rootIndex]]; 27 | siftDown(array, heapSize, largest, compareFn); 28 | } 29 | }; 30 | 31 | /** 32 | * Build max heap. 33 | * @param {*} array to be heapified. 34 | * @param {*} compareFn compare function. 35 | */ 36 | const buildMaxHeap = (array, compareFn) => { 37 | const heapSize = array.length; 38 | for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) { 39 | siftDown(array, heapSize, i, compareFn); 40 | } 41 | } 42 | 43 | /** 44 | * Heap sort algorithm. 45 | * @param {*} array to be sorted. 46 | * @param {*} compareFn compare function. 47 | */ 48 | const HeapSort = (array, compareFn = Comparator.defaultCompareFn) => { 49 | const defaultCompareFn = new Comparator(compareFn); 50 | buildMaxHeap(array, defaultCompareFn); 51 | for (let i = array.length - 1; i > 0; i--) { 52 | [array[0], array[i]] = [array[i], array[0]]; 53 | siftDown(array, i, 0, defaultCompareFn); 54 | } 55 | } 56 | 57 | module.exports = HeapSort; -------------------------------------------------------------------------------- /src/01-intro/06-objects.js: -------------------------------------------------------------------------------- 1 | // Path: src/01-intro/06-objects.js 2 | 3 | /* Object example */ 4 | let obj = new Object(); 5 | obj = {}; 6 | obj = { 7 | name: { 8 | first: 'Gandalf', 9 | last: 'the Grey' 10 | }, 11 | address: 'Middle Earth' 12 | }; 13 | 14 | 15 | /* Class example */ 16 | class Book { 17 | 18 | #percentagePerSale = 0.12; 19 | 20 | constructor(title, pages, isbn) { 21 | this.title = title; 22 | this.pages = pages; 23 | this.isbn = isbn; 24 | } 25 | 26 | get price() { 27 | return this.pages * this.#percentagePerSale; 28 | } 29 | 30 | static copiesSold = 0; 31 | static sellCopy() { 32 | this.copiesSold++; 33 | } 34 | 35 | printIsbn() { 36 | console.log(this.isbn); 37 | } 38 | } 39 | 40 | let myBook = new Book('title', 400, 'isbn'); 41 | 42 | console.log(myBook.title); // outputs the book title 43 | myBook.title = 'new title'; // update the value of the book title 44 | console.log(myBook.title); // outputs the updated value: new title 45 | 46 | console.log(myBook.price); // 48 47 | 48 | console.log(Book.copiesSold); // 0 49 | Book.sellCopy(); 50 | console.log(Book.copiesSold); // 1 51 | 52 | Book.sellCopy(); 53 | console.log(Book.copiesSold); // 2 54 | 55 | class Ebook extends Book { 56 | constructor(title, pages, isbn, format) { 57 | super(title, pages, isbn); 58 | this.format = format; 59 | } 60 | printIsbn() { 61 | console.log('Ebook ISBN:',this.isbn); 62 | } 63 | } 64 | Ebook.sellCopy(); 65 | console.log(Ebook.copiesSold); // 3 66 | 67 | const myBook = new Book('title', 400, 'isbn'); 68 | myBook.printIsbn(); // isbn 69 | const myEbook = new Ebook('Data Structures Ebook', 400, 'isbn 123', 'pdf'); 70 | myEbook.printIsbn(); // Ebook ISBN: isbn 123 71 | 72 | 73 | // to see the output of this file use the command: node src/01-intro/06-objects.js 74 | -------------------------------------------------------------------------------- /src/12-trie/leetcode/longest-common-prefix.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/longest-common-prefix/description/ 2 | // 14. Longest Common Prefix 3 | 4 | function longestCommonPrefix(strs: string[]): string { 5 | if (strs.length === 0) { return ''; } 6 | let prefix = strs[0]; 7 | for (let i = 1; i < strs.length; i++) { 8 | while (strs[i].indexOf(prefix) !== 0) { 9 | prefix = prefix.substring(0, prefix.length - 1); 10 | } 11 | } 12 | return prefix; 13 | } 14 | 15 | // @ts-ignore 16 | class TrieNode { 17 | children: Map = new Map(); 18 | isEndOfWord: boolean = false; 19 | } 20 | 21 | // @ts-ignore 22 | class Trie { 23 | private root: TrieNode; 24 | 25 | constructor() { 26 | this.root = new TrieNode(); 27 | } 28 | 29 | insert(word: string): void { 30 | let node = this.root; 31 | for (let char of word) { 32 | if (!node.children.has(char)) { 33 | node.children.set(char, new TrieNode()); 34 | } 35 | node = node.children.get(char); 36 | } 37 | node.isEndOfWord = true; 38 | } 39 | 40 | findLongestCommonPrefix(): string { 41 | let node = this.root; 42 | let prefix = ''; 43 | while (node.children.size === 1 && !node.isEndOfWord) { 44 | const char = Array.from(node.children.keys())[0]; 45 | prefix += char; 46 | node = node.children.get(char); 47 | } 48 | return prefix; 49 | } 50 | } 51 | 52 | function longestCommonPrefixTrie(strs: string[]): string { 53 | if (strs.length === 0) { 54 | return ''; 55 | } 56 | 57 | let trie = new Trie(); 58 | for (let str of strs) { 59 | trie.insert(str); 60 | } 61 | 62 | return trie.findLongestCommonPrefix(); 63 | } 64 | 65 | console.log(longestCommonPrefix(['flower', 'flow', 'flight'])); // 'fl' 66 | console.log(longestCommonPrefix(['dog', 'racecar', 'car'])); // '' 67 | -------------------------------------------------------------------------------- /src/03-array/10-todo-list-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple TODO List (Array-Based) 6 | 7 | 8 |

My TODO List

9 | 10 | 11 | 12 | 13 |
    14 | 15 | 53 | 54 | -------------------------------------------------------------------------------- /src/08-dictionary-hash/05-hashmap-collision.js: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/05-hashmap-collision.js 2 | 3 | const HashTable = require('./hash-table'); 4 | 5 | const addressBook = new HashTable(); 6 | 7 | // Add contacts 8 | addressBook.put('Ygritte', 'ygritte@email.com'); 9 | addressBook.put('Jonathan', 'jonathan@email.com'); 10 | addressBook.put('Jamie', 'jamie@email.com'); 11 | addressBook.put('Jack', 'jack@email.com'); 12 | addressBook.put('Jasmine', 'jasmine@email.com'); 13 | addressBook.put('Jake', 'jake@email.com'); 14 | addressBook.put('Nathan', 'nathan@email.com'); 15 | addressBook.put('Athelstan', 'athelstan@email.com'); 16 | addressBook.put('Sue', 'sue@email.com'); 17 | addressBook.put('Aethelwulf', 'aethelwulf@email.com'); 18 | addressBook.put('Sargeras', 'sargeras@email.com'); 19 | 20 | // Retrieve the hash code of a contact 21 | console.log(addressBook.hash('Ygritte'), '- Ygritte'); // 4 22 | console.log(addressBook.hash('Jonathan'), '- Jonathan'); // 5 23 | console.log(addressBook.hash('Jamie'), '- Jamie'); // 5 24 | console.log(addressBook.hash('Jack'), '- Jack'); // 7 25 | console.log(addressBook.hash('Jasmine'), '- Jasmine'); // 8 26 | console.log(addressBook.hash('Jake'), '- Jake'); // 9 27 | console.log(addressBook.hash('Nathan'), '- Nathan'); // 10 28 | console.log(addressBook.hash('Athelstan'), '- Athelstan'); // 7 29 | console.log(addressBook.hash('Sue'), '- Sue'); // 5 30 | console.log(addressBook.hash('Aethelwulf'), '- Aethelwulf'); // 5 31 | console.log(addressBook.hash('Sargeras'), '- Sargeras'); // 10 32 | 33 | console.log(addressBook.toString()); 34 | // {4 => ygritte@email.com} 35 | // {5 => aethelwulf@email.com} 36 | // {7 => athelstan@email.com} 37 | // {8 => jasmine@email.com} 38 | // {9 => jake@email.com} 39 | // {10 => sargeras@email.com} 40 | 41 | 42 | // to see the output of this file use the command: node src/08-dictionary-hash/05-hashmap-collision.js -------------------------------------------------------------------------------- /src/13-graph/04-using-dijkstra.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/04-using-dijkstra.js 2 | 3 | const Graph = require('./graph'); 4 | const dijkstra = require('./dijkstra'); 5 | 6 | const flightCosts = [ 7 | // SEA, MDW, DEN, MCO, STL, JFK, ATL 8 | [0, 300, 220, 1000, 0, 0, 0], // SEA 9 | [300, 0, 0, 0, 50, 210, 190], // MDW 10 | [220, 0, 0, 0, 350, 0, 0], // DEN 11 | [1000, 0, 0, 0, 150, 250, 0], // MCO 12 | [0, 50, 350, 150, 0, 0, 0], // STL 13 | [0, 210, 0, 250, 0, 0, 200], // JFK 14 | [0, 190, 0, 0, 0, 200, 0], // ATL 15 | ]; 16 | 17 | console.log('********* Dijkstra\'s Algorithm - Shortest Path ***********'); 18 | 19 | const prices = dijkstra(flightCosts, 0); // SEA 20 | // prices output: 21 | // { 22 | // distances: [ 23 | // 0, 300, 220, 24 | // 500, 350, 510, 25 | // 490 26 | // ], 27 | // predecessors: { 28 | // '0': [ 0 ], 29 | // '1': [ 0, 1 ], 30 | // '2': [ 0, 2 ], 31 | // '3': [ 0, 1, 4, 3 ], 32 | // '4': [ 0, 1, 4 ], 33 | // '5': [ 0, 1, 5 ], 34 | // '6': [ 0, 1, 6 ] 35 | // } 36 | // } 37 | console.log('Prices from SEA to all airports:'); 38 | const airports = ['SEA', 'MDW', 'DEN', 'MCO', 'STL', 'JFK', 'ATL']; 39 | for (let i = 1; i < airports.length; i++) { 40 | const flights = prices.predecessors[i].map(airport => airports[airport]).join(' -> '); 41 | console.log(`SEA -> ${airports[i]}: $${prices.distances[i]} via ${flights}`); 42 | } 43 | // Prices from SEA to all airports: 44 | // SEA -> MDW: $300 via SEA -> MDW 45 | // SEA -> DEN: $220 via SEA -> DEN 46 | // SEA -> MCO: $500 via SEA -> MDW -> STL -> MCO 47 | // SEA -> STL: $350 via SEA -> MDW -> STL 48 | // SEA -> JFK: $510 via SEA -> MDW -> JFK 49 | // SEA -> ATL: $490 via SEA -> MDW -> ATL 50 | 51 | // to see the output of this file use the command: node src/13-graph/04-using-dijkstra.js 52 | -------------------------------------------------------------------------------- /src/13-graph/05-using-floyd-warshall.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/05-using-floyd-warshall.js 2 | 3 | const floydWarshall = require('./floyd-warshall'); 4 | 5 | const INF = Infinity; 6 | const flightCosts = [ 7 | // SEA, MDW, DEN, MCO, STL, JFK, ATL 8 | [INF, 300, 220, 1000, INF, INF, INF], // SEA 9 | [300, INF, INF, INF, 50, 210, 190], // MDW 10 | [220, INF, INF, INF, 350, INF, INF], // DEN 11 | [1000, INF, INF, INF, 150, 250, INF], // MCO 12 | [INF, 50, 350, 150, INF, INF, INF], // STL 13 | [INF, 210, INF, 250, INF, INF, 200], // JFK 14 | [INF, 190, INF, INF, INF, 200, INF], // ATL 15 | ]; 16 | 17 | console.log('********* Floyd-Warshall Algorithm - Shortest Path ***********'); 18 | 19 | const distances = floydWarshall(flightCosts); 20 | console.log('Distances between all airports:'); 21 | 22 | const airports = ['SEA', 'MDW', 'DEN', 'MCO', 'STL', 'JFK', 'ATL']; 23 | console.table(airports.map((airport, i) => { 24 | return airports.reduce((acc, dest, j) => { 25 | acc[dest] = distances[i][j]; 26 | return acc; 27 | }, {}); 28 | })); 29 | 30 | // ┌─────────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ 31 | // │ Airport │ SEA │ MDW │ DEN │ MCO │ STL │ JFK │ ATL │ 32 | // ├─────────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤ 33 | // │ SEA │ 0 │ 300 │ 220 │ 500 │ 350 │ 510 │ 490 │ 34 | // │ MDW │ 300 │ 0 │ 400 │ 200 │ 50 │ 210 │ 190 │ 35 | // │ DEN │ 220 │ 400 │ 0 │ 500 │ 350 │ 610 │ 590 │ 36 | // │ MCO │ 500 │ 200 │ 500 │ 0 │ 150 │ 250 │ 390 │ 37 | // │ STL │ 350 │ 50 │ 350 │ 150 │ 0 │ 260 │ 240 │ 38 | // │ JFK │ 510 │ 210 │ 610 │ 250 │ 260 │ 0 │ 200 │ 39 | // │ ATL │ 490 │ 190 │ 590 │ 390 │ 240 │ 200 │ 0 │ 40 | // └─────────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ 41 | 42 | // to see the output of this file use the command: node src/13-graph/05-using-floyd-warshall.js -------------------------------------------------------------------------------- /src/05-queue-deque/hot-potato.js: -------------------------------------------------------------------------------- 1 | class CircularQueue { 2 | #items = []; 3 | #capacity = 0; 4 | #front = 0; 5 | #rear = -1; 6 | #size = 0; 7 | 8 | constructor(capacity) { 9 | this.#items = new Array(capacity); 10 | this.#capacity = capacity; 11 | } 12 | 13 | enqueue(item) { 14 | if (this.isFull()) { 15 | throw new Error("Queue is full"); 16 | } 17 | this.#rear = (this.#rear + 1) % this.#capacity; 18 | this.#items[this.#rear] = item; 19 | this.#size++; 20 | } 21 | 22 | dequeue() { 23 | if (this.isEmpty()) { throw new Error("Queue is empty"); } 24 | 25 | const item = this.#items[this.#front]; 26 | this.#size--; 27 | 28 | if (this.isEmpty()) { 29 | this.#front = 0; 30 | this.#rear = -1; 31 | } else { 32 | this.#front = (this.#front + 1) % this.#capacity; 33 | } 34 | 35 | return item; 36 | } 37 | 38 | isEmpty() { return this.#size === 0; } 39 | isFull() { return this.#size === this.#capacity; } 40 | get size() { return this.#size; } 41 | } 42 | 43 | // Hot Potato Game Function 44 | function hotPotato(players, numPasses) { 45 | const queue = new CircularQueue(players.length); 46 | for (const player of players) { 47 | queue.enqueue(player); 48 | } 49 | 50 | while (queue.size > 1) { 51 | for (let i = 0; i < numPasses; i++) { 52 | queue.enqueue(queue.dequeue()); 53 | } 54 | console.log(`${queue.dequeue()} is eliminated!`); 55 | } 56 | 57 | return queue.dequeue(); // The winner 58 | } 59 | 60 | // Example Usage 61 | const players = ["Violet", "Feyre", "Poppy", "Oraya", "Aelin"]; 62 | const winner = hotPotato(players, 7); 63 | console.log(`The winner is: ${winner}!`); 64 | 65 | // Output 66 | // Poppy is eliminated! 67 | // Feyre is eliminated! 68 | // Aelin is eliminated! 69 | // Oraya is eliminated! 70 | // The winner is: Violet! 71 | 72 | // to see the output of this file use the command: node src/05-queue-deque/hot-potato.js -------------------------------------------------------------------------------- /src/13-graph/kruskal.js: -------------------------------------------------------------------------------- 1 | 2 | const INF = Number.MAX_SAFE_INTEGER; 3 | 4 | const kruskal = (graph) => { 5 | const { length } = graph; 6 | const parent = []; // Stores the MST 7 | let ne = 0; // Number of edges in the MST 8 | let a; let b; let u; let v; 9 | const cost = initializeCost(graph); // Create a copy of the graph 10 | 11 | // While the MST has fewer edges than the total number of vertices - 1 12 | while (ne < length - 1) { 13 | for (let i = 0, min = INF; i < length; i++) { 14 | for (let j = 0; j < length; j++) { 15 | // Find the edge with the minimum cost 16 | if (cost[i][j] < min) { 17 | min = cost[i][j]; 18 | a = u = i; 19 | b = v = j; 20 | } 21 | } 22 | } 23 | 24 | u = find(u, parent); // Find the set of vertex u 25 | v = find(v, parent); // Find the set of vertex v 26 | 27 | // If adding the edge doesn't create a cycle, add it to the MST 28 | if (union(u, v, parent)) { 29 | ne++; 30 | } 31 | 32 | cost[a][b] = cost[b][a] = INF; // Remove the edge from the cost matrix 33 | } 34 | 35 | return parent; // Return the MST 36 | }; 37 | 38 | // Helper function to initialize the cost matrix 39 | const initializeCost = (graph) => { 40 | const { length } = graph; 41 | const cost = Array(length).fill(null).map(() => Array(length).fill(null)); 42 | 43 | for (let i = 0; i < length; i++) { 44 | for (let j = 0; j < length; j++) { 45 | cost[i][j] = graph[i][j] || INF; 46 | } 47 | } 48 | 49 | return cost; 50 | }; 51 | 52 | // Helper function to find the set of an element i 53 | const find = (i, parent) => { 54 | while (parent[i]) { 55 | i = parent[i]; 56 | } 57 | return i; 58 | }; 59 | 60 | // Helper function to union two sets of i and j 61 | const union = (i, j, parent) => { 62 | if (i !== j) { 63 | parent[j] = i; 64 | return true; 65 | } 66 | return false; 67 | }; 68 | 69 | module.exports = kruskal; -------------------------------------------------------------------------------- /src/12-trie/trie.ts: -------------------------------------------------------------------------------- 1 | class TrieNode { 2 | children: Map; 3 | isEndOfWord: boolean; 4 | 5 | constructor() { 6 | this.children = new Map(); 7 | this.isEndOfWord = false; 8 | } 9 | } 10 | 11 | class Trie { 12 | private root: TrieNode; 13 | 14 | constructor() { 15 | this.root = new TrieNode(); 16 | } 17 | 18 | insert(word: string): void { 19 | let node = this.root; 20 | for (let char of word) { 21 | if (!node.children.has(char)) { 22 | node.children.set(char, new TrieNode()); 23 | } 24 | node = node.children.get(char)!; 25 | } 26 | node.isEndOfWord = true; 27 | } 28 | 29 | search(word: string): boolean { 30 | let node = this.root; 31 | for (let char of word) { 32 | if (!node.children.has(char)) { 33 | return false; 34 | } 35 | node = node.children.get(char)!; 36 | } 37 | return node.isEndOfWord; 38 | } 39 | 40 | startsWith(prefix: string): boolean { 41 | let node = this.root; 42 | for (let char of prefix) { 43 | if (!node.children.has(char)) { 44 | return false; 45 | } 46 | node = node.children.get(char)!; 47 | } 48 | return true; 49 | } 50 | 51 | remove(word: string): boolean { 52 | return this.#removeWord(this.root, word, 0); 53 | } 54 | 55 | #removeWord(node: TrieNode, word: string, index: number): boolean { 56 | if (index === word.length) { 57 | if (!node.isEndOfWord) return false; 58 | node.isEndOfWord = false; 59 | return node.children.size === 0; 60 | } 61 | 62 | const char = word[index]; 63 | if (!node.children.has(char)) { 64 | return false; 65 | } 66 | 67 | const shouldDeleteCurrentNode = this.#removeWord(node.children.get(char)!, word, index + 1); 68 | if (shouldDeleteCurrentNode) { 69 | node.children.delete(char); 70 | return node.children.size === 0; 71 | } 72 | 73 | return false; 74 | } 75 | } 76 | 77 | export default Trie; -------------------------------------------------------------------------------- /src/02-bigOnotation/03-exercises.js: -------------------------------------------------------------------------------- 1 | // Path: src/02-bigOnotation/03-exercises.js 2 | 3 | /* What is the time and space complexities for each of the following functions. 4 | Try them with different inputs to explore how they behave with different inputs */ 5 | 6 | // time complexity: O(1) - Constant Time 7 | // space complexity: O(1) - Constant Space 8 | const oddOrEven = (array) => array.length % 2 === 0 ? 'even' : 'odd'; 9 | 10 | console.log(oddOrEven([1, 2, 3, 4, 5])); // odd 11 | console.log(oddOrEven([1, 2, 3, 4, 5, 6])); // even 12 | 13 | // time complexity: O(n) - Linear Time 14 | // space complexity: O(1) - Constant Space 15 | function calculateAverage(array) { 16 | let sum = 0; 17 | for (let i = 0; i < array.length; i++) { 18 | sum += array[i]; 19 | } 20 | return sum / array.length; 21 | } 22 | 23 | console.log(calculateAverage([1, 2, 3, 4, 5])); // 3 24 | console.log(calculateAverage([1, 2, 3, 4, 5, 6])); // 3.5 25 | 26 | // time complexity: O(n^2) - Quadratic Time 27 | // space complexity: O(1) - Constant Space 28 | function hasCommonElements(array1, array2) { 29 | for (let i = 0; i < array1.length; i++) { 30 | for (let j = 0; j < array2.length; j++) { 31 | if (array1[i] === array2[j]) { 32 | return true; 33 | } 34 | } 35 | } 36 | return false; 37 | } 38 | 39 | console.log(hasCommonElements([1, 2, 3, 4, 5], [6, 7, 8, 9, 10])); // false 40 | console.log(hasCommonElements([1, 2, 3, 4, 5], [5, 6, 7, 8, 9])); // true 41 | 42 | // time complexity: O(n) - Linear Time 43 | // space complexity: O(n) - Linear Space 44 | function getOddNumbers(array) { 45 | const result = []; 46 | for (let i = 0; i < array.length; i++) { 47 | if (array[i] % 2 !== 0) { 48 | result.push(array[i]); 49 | } 50 | } 51 | return result; 52 | } 53 | 54 | console.log(getOddNumbers([1, 2, 3, 4, 5])); // [1, 3, 5] 55 | console.log(getOddNumbers([1, 2, 3, 4, 5, 6])); // [1, 3, 5] 56 | 57 | // to see the output of this file use the command: node src/02-bigOnotation/03-exercises.js -------------------------------------------------------------------------------- /src/05-queue-deque/leetcode/ex.md: -------------------------------------------------------------------------------- 1 | Explanation 2 | 3 | Count Preferences: 4 | 5 | Create an array counts to store how many students want each sandwich type. 6 | Iterate through the students array. For each student, increment the corresponding count in the counts array. 7 | Process Sandwiches: 8 | 9 | Iterate through the sandwiches array. For each sandwich: 10 | If the count for that sandwich type is zero, it means no one left wants it. Break the loop. 11 | Otherwise, decrement the count for that sandwich type, as one student took it. 12 | Return Result: 13 | 14 | After processing all sandwiches (or breaking early), the remaining counts in the counts array represent the students who didn't get their preferred sandwich. Add these counts and return the result. 15 | Why This is More Efficient 16 | 17 | Avoiding Array Manipulation: The optimized version avoids repeated shifting and pushing of elements in the students array. This reduces the computational overhead, especially with large inputs. 18 | 19 | Early Termination: By checking if the count of a sandwich type reaches zero, the loop can exit early, potentially saving many iterations if a lot of sandwiches remain that no one wants. 20 | 21 | Simpler Logic: The logic is more straightforward, making it easier to understand and maintain. 22 | 23 | Example 24 | 25 | Let's use the same example as before: 26 | 27 | students = [1, 1, 0, 0] 28 | sandwiches = [0, 1, 0, 1] 29 | counts becomes [2, 2] (two students for each type). 30 | The first sandwich (0) is taken, counts is now [1, 2]. 31 | The second sandwich (1) is taken, counts is now [1, 1]. 32 | This continues until all sandwiches are gone and counts becomes [0, 0]. 33 | The function returns 0 + 0 = 0. 34 | Key Improvement 35 | 36 | The main optimization comes from avoiding the costly includes check and array manipulations inside the loop. Instead, we directly track the number of students who still want each sandwich type. This allows us to efficiently determine when no one wants a particular sandwich anymore, leading to early termination. -------------------------------------------------------------------------------- /src/04-stack/stack.js: -------------------------------------------------------------------------------- 1 | // src/04-stack/stack.js 2 | 3 | // @ts-ignore 4 | class Stack { 5 | 6 | // private property 7 | #items = []; // {1} 8 | 9 | /** 10 | * Adds a new element to the top of the stack 11 | * @param {*} item - new element to be added to the stack 12 | */ 13 | push(item) { 14 | this.#items.push(item); 15 | } 16 | 17 | /** 18 | * Removes the top element from the stack and returns it. If the stack is empty, undefined is returned and the stack is not modified. 19 | */ 20 | pop() { 21 | return this.#items.pop(); 22 | } 23 | 24 | /** 25 | * Returns the top element of the stack without removing it. The stack is not modified (it does not remove the element; it only returns the element for information purposes). 26 | */ 27 | peek() { 28 | return this.#items[this.#items.length - 1]; 29 | } 30 | 31 | /** 32 | * Returns true if the stack does not contain any elements and false if the size of the stack is bigger than 0. 33 | */ 34 | isEmpty() { 35 | return this.#items.length === 0; 36 | } 37 | 38 | /** 39 | * Returns the number of elements in the stack. It is similar to the length property of an array. 40 | */ 41 | get size() { 42 | return this.#items.length; 43 | } 44 | 45 | /** 46 | * Removes all the elements from the stack 47 | */ 48 | clear() { 49 | /* while (!this.isEmpty()) { 50 | this.pop(); 51 | } */ 52 | this.#items = []; 53 | } 54 | 55 | /** 56 | * Returns a string representation of the stack, bottom to top, without modifying the stack 57 | */ 58 | toString() { 59 | if (this.isEmpty()) { 60 | return 'Empty Stack'; 61 | } else { 62 | return this.#items.map(item => { // {1} 63 | if (typeof item === 'object' && item !== null) { // {2} 64 | return JSON.stringify(item); // Handle objects 65 | } else { 66 | return item.toString(); // Handle other types {3} 67 | } 68 | }).join(', '); // {4} 69 | } 70 | } 71 | } 72 | 73 | // export node module so it can be used in different files 74 | // CommonJS export 75 | module.exports = Stack; -------------------------------------------------------------------------------- /src/13-graph/bfs.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/bfs.js 2 | 3 | const Colors = { 4 | WHITE: 0, 5 | GREY: 1, 6 | BLACK: 2 7 | }; 8 | 9 | const initializeColor = vertices => { 10 | const color = {}; 11 | vertices.forEach(vertex => { 12 | color[vertex] = Colors.WHITE; 13 | }); 14 | return color; 15 | }; 16 | 17 | const breadthFirstSearch = (graph, startVertex, callback) => { 18 | const vertices = graph.vertices; 19 | const adjList = graph.adjList; 20 | const color = initializeColor(vertices); 21 | const queue = [startVertex]; 22 | 23 | while (queue.length) { 24 | const visiting = queue.shift(); 25 | const neighbors = adjList.get(visiting); 26 | color[visiting] = Colors.GREY; 27 | for (let i = 0; i < neighbors.length; i++) { 28 | const neighbor = neighbors[i]; 29 | if (color[neighbor] === Colors.WHITE) { 30 | color[neighbor] = Colors.GREY; 31 | queue.push(neighbor); 32 | } 33 | } 34 | color[visiting] = Colors.BLACK; 35 | if (callback) { 36 | callback(visiting); 37 | } 38 | } 39 | }; 40 | 41 | const bfsShortestPath = (graph, startVertex) => { 42 | const vertices = graph.vertices; 43 | const adjList = graph.adjList; 44 | const color = {}; 45 | const dist = {}; 46 | const pred = {}; 47 | const queue = [startVertex]; 48 | 49 | vertices.forEach(vertex => { 50 | color[vertex] = Colors.WHITE; 51 | dist[vertex] = 0; 52 | pred[vertex] = null; 53 | }); 54 | 55 | while (queue.length) { 56 | const visiting = queue.shift(); 57 | const neighbors = adjList.get(visiting); 58 | color[visiting] = Colors.GREY; 59 | for (let i = 0; i < neighbors.length; i++) { 60 | const neighbor = neighbors[i]; 61 | if (color[neighbor] === Colors.WHITE) { 62 | color[neighbor] = Colors.GREY; 63 | dist[neighbor] = dist[visiting] + 1; 64 | pred[neighbor] = visiting; 65 | queue.push(neighbor); 66 | } 67 | } 68 | color[visiting] = Colors.BLACK; 69 | } 70 | 71 | return { 72 | distances: dist, 73 | predecessors: pred 74 | }; 75 | }; 76 | 77 | module.exports = { breadthFirstSearch , bfsShortestPath }; -------------------------------------------------------------------------------- /src/10-tree/03-pre-order-traversal.js: -------------------------------------------------------------------------------- 1 | // src/10-tree/03-pre-order-traversal.js 2 | const BinarySearchTree = require('./binary-search-tree'); 3 | 4 | class Employee { 5 | constructor(id, name, title) { 6 | this.id = id; 7 | this.name = name; 8 | this.title = title; 9 | } 10 | } 11 | 12 | // create title constant 13 | const TITLE = { 14 | CEO: 1, 15 | VP: 2, 16 | MANAGER: 3, 17 | STAFF: 4 18 | } 19 | 20 | // create employee comparator to compare title 21 | const employeeComparator = (a, b) => a.id - b.id; 22 | 23 | const employeeTree = new BinarySearchTree(employeeComparator); 24 | 25 | employeeTree.insert(new Employee(10, 'Gandalf', TITLE.CEO)); 26 | employeeTree.insert(new Employee(5, 'Frodo', TITLE.VP)); 27 | employeeTree.insert(new Employee(3,'Legolas', TITLE.MANAGER)); 28 | employeeTree.insert(new Employee(1, 'Aragorn', TITLE.STAFF)); 29 | employeeTree.insert(new Employee(4, 'Gimli', TITLE.STAFF)); 30 | 31 | employeeTree.insert(new Employee(14, 'Arya', TITLE.VP)); 32 | employeeTree.insert(new Employee(12, 'John', TITLE.MANAGER)); 33 | employeeTree.insert(new Employee(11, 'Brienne', TITLE.STAFF)); 34 | employeeTree.insert(new Employee(13, 'Tyrion', TITLE.STAFF)); 35 | 36 | // pre order traversal 37 | const sendEmergencyNotification = (employee, message) => { 38 | console.log(`Notifying ${employee.name}: ${message}`); 39 | } 40 | const emergencyMessage = 'Tornado warning in the area. Seek shelter immediately!'; 41 | employeeTree.preOrderTraverse((node) => sendEmergencyNotification(node, emergencyMessage)); 42 | 43 | // Notifying Gandalf: Tornado warning in the area. Seek shelter immediately! 44 | // Notifying Frodo: Tornado warning in the area. Seek shelter immediately! 45 | // Notifying Legolas: Tornado warning in the area. Seek shelter immediately! 46 | // Notifying Arya: Tornado warning in the area. Seek shelter immediately! 47 | // Notifying Aragorn: Tornado warning in the area. Seek shelter immediately! 48 | // Notifying John: Tornado warning in the area. Seek shelter immediately! 49 | // Notifying Gimli: Tornado warning in the area. Seek shelter immediately! 50 | // Notifying Brienne: Tornado warning in the area. Seek shelter immediately! 51 | // Notifying Tyrion: Tornado warning in the area. Seek shelter immediately! 52 | 53 | // to see the output of this file use the command: node src/10-tree/03-pre-order-traversal.js 54 | -------------------------------------------------------------------------------- /src/13-graph/03-using-dfs.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/03-using-dfs.js 2 | 3 | const Graph = require('./graph'); 4 | const { depthFirstSearch, enhancedDepthFirstSearch } = require('./dfs'); 5 | 6 | const caveSystem = new Graph(); 7 | 8 | const caves = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']; 9 | 10 | caves.forEach(cave => caveSystem.addVertex(cave)); 11 | 12 | 13 | caveSystem.addEdge('A', 'B'); 14 | caveSystem.addEdge('A', 'C'); 15 | caveSystem.addEdge('A', 'D'); 16 | caveSystem.addEdge('C', 'D'); 17 | caveSystem.addEdge('C', 'G'); 18 | caveSystem.addEdge('D', 'G'); 19 | caveSystem.addEdge('D', 'H'); 20 | caveSystem.addEdge('B', 'E'); 21 | caveSystem.addEdge('B', 'F'); 22 | caveSystem.addEdge('E', 'I'); 23 | 24 | console.log('********* DFS - cave ***********'); 25 | depthFirstSearch(caveSystem, (cave) => console.log('Visited cave:',cave)); 26 | 27 | // ********* DFS - cave *********** 28 | // Visited cave: A 29 | // Visited cave: B 30 | // Visited cave: E 31 | // Visited cave: I 32 | // Visited cave: F 33 | // Visited cave: C 34 | // Visited cave: D 35 | // Visited cave: G 36 | // Visited cave: H 37 | 38 | console.log('********* Enhanced DFS - cave ***********'); 39 | const result = enhancedDepthFirstSearch(caveSystem); 40 | 41 | console.log(result); 42 | 43 | console.log('********* Topological sort using DFS ***********'); 44 | 45 | const tasks = new Graph(true); // this is a directed graph 46 | ['A', 'B', 'C', 'D', 'E', 'F'].forEach(task => tasks.addVertex(task)); 47 | // add the arrows, task dependencies: 48 | tasks.addEdge('A', 'C'); 49 | tasks.addEdge('A', 'D'); 50 | tasks.addEdge('B', 'D'); 51 | tasks.addEdge('B', 'E'); 52 | tasks.addEdge('C', 'F'); 53 | tasks.addEdge('F', 'E'); 54 | 55 | // DFS traversal 56 | const dfsTasks = enhancedDepthFirstSearch(tasks); 57 | console.log(dfsTasks); 58 | // { 59 | // discovery: { A: 1, B: 11, C: 2, D: 8, E: 4, F: 3 }, 60 | // finished: { A: 10, B: 12, C: 7, D: 9, E: 5, F: 6 }, 61 | // predecessors: { A: null, B: null, C: 'A', D: 'A', E: 'F', F: 'C' } 62 | // } 63 | 64 | // sort tasks in decreasing order of finish time 65 | // dfsTasks.finished = { A: 10, B: 12, C: 7, D: 9, E: 5, F: 6 } 66 | const sortedTasks = Object.keys(dfsTasks.finished).sort((a, b) => dfsTasks.finished[b] - dfsTasks.finished[a]); 67 | console.log(sortedTasks); 68 | // [ 'B', 'A', 'D', 'C', 'F', 'E' ] 69 | 70 | // to see the output of this file use the command: node src/13-graph/03-using-dfs.js -------------------------------------------------------------------------------- /src/03-array/02-adding-removing-elements.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/02-adding-removing-elements.ts 2 | 3 | // @ts-ignore 4 | let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 5 | 6 | numbers[numbers.length] = 10; 7 | 8 | // using push method 9 | numbers.push(11); 10 | numbers.push(12, 13); 11 | 12 | // inserting elements at the beginning 13 | // @ts-ignore 14 | Array.prototype.insertAtBeginning = function (value) { 15 | for (let i = this.length; i >= 0; i--) { 16 | this[i] = this[i - 1]; 17 | } 18 | this[0] = value; 19 | }; 20 | // @ts-ignore 21 | numbers.insertAtBeginning(-1); 22 | 23 | // using unshift method 24 | numbers.unshift(-2); 25 | numbers.unshift(-4, -3); 26 | 27 | // removing elements from the end 28 | numbers.pop(); // number 13 is removed 29 | // console.log('Removed element: ', numbers.pop()); // Removed element: 13 30 | console.log('array length: ', numbers.length); // array length: 17 31 | 32 | // removing elements from the beginning 33 | for (let i = 0; i < numbers.length; i++) { 34 | // numbers[i] = numbers[i + 1]; 35 | } 36 | 37 | // removing elements from the beginning - educational purposes only 38 | // @ts-ignore 39 | Array.prototype.reIndex = function (myArray) { 40 | const newArray = []; 41 | for (let i = 0; i < myArray.length; i++) { 42 | if (myArray[i] !== undefined) { 43 | newArray.push(myArray[i]); 44 | } 45 | } 46 | return newArray; 47 | } 48 | // remove first position manually and reIndex 49 | // @ts-ignore 50 | Array.prototype.removeFromBeginning = function () { 51 | for (let i = 0; i < this.length; i++) { 52 | this[i] = this[i + 1]; 53 | } 54 | // @ts-ignore 55 | return this.reIndex(this); 56 | }; 57 | // @ts-ignore 58 | // numbers = numbers.removeFromBeginning(); 59 | 60 | // using shift method 61 | numbers.shift(); 62 | console.log('numbers after shift: ', numbers); 63 | console.log('array length: ', numbers.length); // array length: 16 64 | 65 | // adding and removing elements from a specific position 66 | // using the splice method 67 | numbers.splice(5, 3); // removes 3 elements starting from the 5th position 68 | console.log('numbers: ', numbers); // numbers: [ -3, -2, -1, 0, 1, 5, 6, 7, 8, 9, 10, 11, 12 ] 69 | 70 | // adding elements, 2, 3 and 4 at the 5th position 71 | numbers.splice(5, 0, 2, 3, 4); 72 | 73 | // to see the output of this file use the command: node src/03-array/02-adding-removing-elements.ts 74 | -------------------------------------------------------------------------------- /src/05-queue-deque/__test__/queue.test.ts: -------------------------------------------------------------------------------- 1 | import {describe, expect, test, beforeEach} from '@jest/globals'; 2 | import Queue from '../queue'; 3 | 4 | describe('Queue', () => { 5 | let queue; 6 | 7 | beforeEach(() => { 8 | queue = new Queue(); 9 | }); 10 | 11 | test('should enqueue and dequeue elements correctly', () => { 12 | queue.enqueue(1); 13 | queue.enqueue(2); 14 | queue.enqueue(3); 15 | 16 | expect(queue.dequeue()).toBe(1); 17 | expect(queue.dequeue()).toBe(2); 18 | expect(queue.dequeue()).toBe(3); 19 | expect(queue.isEmpty()).toBe(true); 20 | }); 21 | 22 | test('should return undefined when dequeueing from an empty queue', () => { 23 | expect(queue.dequeue()).toBeUndefined(); 24 | }); 25 | 26 | test('should return the correct size after enqueueing and dequeueing elements', () => { 27 | queue.enqueue(1); 28 | queue.enqueue(2); 29 | queue.enqueue(3); 30 | 31 | queue.dequeue(); 32 | queue.dequeue(); 33 | 34 | expect(queue.size).toBe(1); 35 | }); 36 | 37 | test('should return the correct front element after enqueueing and dequeueing elements', () => { 38 | queue.enqueue(1); 39 | queue.enqueue(2); 40 | queue.enqueue(3); 41 | 42 | queue.dequeue(); 43 | 44 | expect(queue.front()).toBe(2); 45 | }); 46 | 47 | test('should return undefined when calling front on an empty queue', () => { 48 | expect(queue.front()).toBeUndefined(); 49 | }); 50 | 51 | test('should clear the queue and return the correct size', () => { 52 | queue.enqueue(1); 53 | queue.enqueue(2); 54 | queue.enqueue(3); 55 | 56 | queue.clear(); 57 | 58 | expect(queue.isEmpty()).toBe(true); 59 | expect(queue.size).toBe(0); 60 | }); 61 | 62 | test('should convert the queue to a string', () => { 63 | queue.enqueue(1); 64 | queue.enqueue(2); 65 | queue.enqueue(3); 66 | 67 | expect(queue.toString()).toBe('1, 2, 3'); 68 | }); 69 | 70 | test('should convert the queue to a string with empty queue', () => { 71 | expect(queue.toString()).toBe('Empty Queue'); 72 | }); 73 | 74 | test('should convert the queue to a string with object', () => { 75 | queue.enqueue({name: 'John', age: 30}); 76 | queue.enqueue({name: 'Jane', age: 25}); 77 | queue.enqueue({name: 'Doe', age: 40}); 78 | 79 | expect(queue.toString()).toBe( 80 | '{"name":"John","age":30}, {"name":"Jane","age":25}, {"name":"Doe","age":40}', 81 | ); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /src/03-array/01-arrays.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/01-arrays.ts 2 | 3 | /* Arrays Intro */ 4 | const averageTempJan = 12; 5 | const averageTempFeb = 15; 6 | const averageTempMar = 18; 7 | const averageTempApr = 20; 8 | const averageTempMay = 25; 9 | 10 | const averageTemp = [12, 15, 18, 20, 25]; 11 | // or 12 | averageTemp[0] = 12; 13 | averageTemp[1] = 15; 14 | averageTemp[2] = 18; 15 | averageTemp[3] = 20; 16 | averageTemp[4] = 25; 17 | 18 | console.log('averageTempJan', averageTempJan); 19 | console.log('averageTempFeb', averageTempFeb); 20 | console.log('averageTempMar', averageTempMar); 21 | console.log('averageTempApr', averageTempApr); 22 | console.log('averageTempMay', averageTempMay); 23 | 24 | console.log('averageTemp[0]', averageTemp[0]); 25 | console.log('averageTemp[1]', averageTemp[1]); 26 | console.log('averageTemp[2]', averageTemp[2]); 27 | console.log('averageTemp[3]', averageTemp[3]); 28 | console.log('averageTemp[4]', averageTemp[4]); 29 | 30 | /* Creating and initializing arrays */ 31 | let daysOfWeek = new Array(); // {1} 32 | daysOfWeek = new Array(7); // {2} 33 | daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); // {3} 34 | 35 | // preferred 36 | daysOfWeek = []; // {4} 37 | daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; // {5} 38 | 39 | console.log('daysOfWeek.length', daysOfWeek.length); // output: 7 40 | 41 | for (let i = 0; i < daysOfWeek.length; i++) { 42 | console.log(`daysOfWeek[${i}]`, daysOfWeek[i]); 43 | } 44 | 45 | /* fibonacci numbers */ 46 | // Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 47 | const fibonacci = []; // {1} 48 | fibonacci[1] = 1; // {2} 49 | fibonacci[2] = 1; // {3} 50 | 51 | // create the fibonacci sequence starting from the 3rd element 52 | for (let i = 3; i < 20; i++) { 53 | fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2]; // //{4} 54 | } 55 | 56 | // display the fibonacci sequence 57 | for (let i = 1; i < fibonacci.length; i++) { // {5} 58 | console.log(`fibonacci[${i}]`, fibonacci[i]); // {6} 59 | } 60 | 61 | // instead of {5} and {6} we can use 62 | console.log('fibonacci', fibonacci); 63 | 64 | // Using the for..in loop 65 | for (const i in fibonacci) { 66 | console.log(`fibonacci[${i}]`, fibonacci[i]); 67 | } 68 | 69 | // Using the for..of loop 70 | for (const value of fibonacci) { 71 | console.log('value', value); 72 | } 73 | 74 | // to see the output of this file use the command: node src/03-array/01-arrays.ts -------------------------------------------------------------------------------- /src/02-bigOnotation/01-big-o-intro.js: -------------------------------------------------------------------------------- 1 | // Path: src/02-bigOnotation/01-big-o-intro.js 2 | 3 | // O(1) - Constant Time 4 | function secondsInDays(numberOfDays) { 5 | if (numberOfDays <= 0 || !Number.isInteger(numberOfDays)) { 6 | throw new Error('Invalid number of days'); 7 | } 8 | return 60 * 60 * 24 * numberOfDays; 9 | } 10 | 11 | console.log('O(1) - Constant Time'); 12 | console.log('Seconds in 1 day: ', secondsInDays(1)); // 86400 13 | console.log('Seconds in 10 days: ', secondsInDays(10)); // 864000 14 | console.log('Seconds in 100 days: ', secondsInDays(100)); // 8640000 15 | 16 | // O(n) - Linear Time 17 | function calculateTotalExpenses(monthlyExpenses) { 18 | let total = 0; 19 | for (let i = 0; i < monthlyExpenses.length; i++) { 20 | total += monthlyExpenses[i]; 21 | } 22 | return total; 23 | } 24 | 25 | console.log('*******************'); 26 | console.log('O(n) - Linear Time'); 27 | console.log('January: ', calculateTotalExpenses([100, 200, 300])); // 600 28 | console.log('February: ', calculateTotalExpenses([200, 300, 400])); // 900 29 | console.log('March: ', calculateTotalExpenses([30, 40, 50, 100, 50])); // 270 30 | 31 | // O(n^2) - Quadratic Time 32 | function calculateExpensesMatrix(monthlyExpenses) { 33 | let total = 0; 34 | for (let i = 0; i < monthlyExpenses.length; i++) { 35 | for (let j = 0; j < monthlyExpenses[i].length; j++) { 36 | total += monthlyExpenses[i][j]; 37 | } 38 | } 39 | return total; 40 | } 41 | 42 | console.log('************************'); 43 | console.log('O(n^2) - Quadratic Time'); 44 | const monthlyExpenses = [ 45 | [100, 105, 100, 115, 120, 135], 46 | [180, 185, 185, 185, 200, 210], 47 | [30, 30, 30, 30, 30, 30], 48 | [2000, 2000, 2000, 2000, 2000, 2000], 49 | [600, 620, 610, 600, 620, 600], 50 | [150, 100, 130, 200, 150, 100] 51 | ]; 52 | console.log('Total expenses: ', calculateExpensesMatrix(monthlyExpenses)); // 18480 53 | 54 | // calculating the time complexity of the function calculateExpensesMatrix 55 | function multiplicationTable(num, x) { 56 | let s = ''; 57 | let numberOfAsterisks = num * x; 58 | for (let i = 1; i <= numberOfAsterisks; i++) { 59 | s += '*'; 60 | } 61 | console.log(s); 62 | 63 | for (let i = 1; i <= num; i++) { 64 | console.log(`Multiplication table for ${i} with x = ${x}`); 65 | for (let j = 1; j <= x; j++) { 66 | console.log(`${i} * ${j} = `, i * j); 67 | } 68 | } 69 | } 70 | 71 | 72 | // to see the output of this file use the command: node src/02-bigOnotation/01-big-o-intro.js -------------------------------------------------------------------------------- /src/03-array/01-arrays.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/01-arrays.js 2 | 3 | /* Arrays Intro */ 4 | const averageTempJan = 12; 5 | const averageTempFeb = 15; 6 | const averageTempMar = 18; 7 | const averageTempApr = 20; 8 | const averageTempMay = 25; 9 | 10 | const averageTemp = [12, 15, 18, 20, 25]; 11 | // or 12 | averageTemp[0] = 12; 13 | averageTemp[1] = 15; 14 | averageTemp[2] = 18; 15 | averageTemp[3] = 20; 16 | averageTemp[4] = 25; 17 | 18 | console.log('averageTempJan', averageTempJan); 19 | console.log('averageTempFeb', averageTempFeb); 20 | console.log('averageTempMar', averageTempMar); 21 | console.log('averageTempApr', averageTempApr); 22 | console.log('averageTempMay', averageTempMay); 23 | 24 | console.log('averageTemp[0]', averageTemp[0]); 25 | console.log('averageTemp[1]', averageTemp[1]); 26 | console.log('averageTemp[2]', averageTemp[2]); 27 | console.log('averageTemp[3]', averageTemp[3]); 28 | console.log('averageTemp[4]', averageTemp[4]); 29 | 30 | /* Creating and initializing arrays */ 31 | // preferred 32 | let daysOfWeek = []; // creates an empty array 33 | daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 34 | 35 | console.log('daysOfWeek.length:', daysOfWeek.length); // daysOfWeek.length: 7 36 | 37 | // different syntax, avoid: 38 | daysOfWeek = new Array(); // creates an empty array 39 | daysOfWeek = new Array(7); // pre-defined length of 7 40 | daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); 41 | 42 | // ---- interating over arrays ---- 43 | for (let i = 0; i < daysOfWeek.length; i++) { 44 | console.log(`daysOfWeek[${i}]`, daysOfWeek[i]); 45 | } 46 | 47 | /* fibonacci numbers */ 48 | // Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 49 | const fibonacci = []; 50 | fibonacci[0] = 0; 51 | fibonacci[1] = 1; 52 | 53 | // create the fibonacci sequence starting from the 2nd element 54 | for (let i = 2; i < 20; i++) { 55 | fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2]; 56 | } 57 | 58 | // display the fibonacci sequence 59 | for (let i = 1; i < fibonacci.length; i++) { 60 | console.log(`fibonacci[${i}]`, fibonacci[i]); 61 | } 62 | 63 | // instead of {5} and {6} we can use 64 | console.log('fibonacci', fibonacci); 65 | 66 | // Using the for..in loop 67 | for (const i in fibonacci) { 68 | console.log(`fibonacci[${i}]`, fibonacci[i]); 69 | } 70 | 71 | // Using the for..of loop 72 | for (const value of fibonacci) { 73 | console.log('value', value); 74 | } 75 | 76 | // to see the output of this file use the command: node src/03-array/01-arrays.js -------------------------------------------------------------------------------- /src/08-dictionary-hash/hash-table-separate-chaining.js: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/hash-table-separate-chaining.js 2 | 3 | const LinkedList = require('../06-linked-list/linked-list_'); 4 | 5 | class HashTableSeparateChaining { 6 | #table = []; 7 | 8 | #loseLoseHashCode(key) { 9 | if (typeof key !== 'string') { 10 | key = this.#elementToString(key); 11 | } 12 | const hash = key.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); 13 | return hash % 37; // mod to reduce the hash code 14 | } 15 | 16 | hash(key) { 17 | return this.#loseLoseHashCode(key); 18 | } 19 | 20 | 21 | put(key, value) { 22 | if (key != null && value != null) { 23 | const index = this.hash(key); // Get hash code (index) 24 | 25 | if (this.#table[index] == null) { 26 | this.#table[index] = new LinkedList(); // Create linked list if needed 27 | } 28 | 29 | this.#table[index].append({key, value}); // Append to linked list 30 | return true; 31 | } 32 | return false; 33 | } 34 | 35 | get(key) { 36 | const index = this.hash(key); 37 | const linkedList = this.#table[index]; 38 | if (linkedList != null) { 39 | linkedList.forEach((element) => { 40 | if (element.key === key) { 41 | return element.value; 42 | } 43 | }); 44 | } 45 | return undefined; // key not found 46 | } 47 | 48 | remove(key) { 49 | const index = this.hash(key); 50 | const linkedList = this.#table[index]; 51 | if (linkedList != null) { 52 | const compareFunction = (a, b) => a.key === b.key; 53 | const toBeRemovedIndex = linkedList.indexOf({key}, compareFunction); 54 | if (toBeRemovedIndex >= 0) { 55 | linkedList.removeAt(toBeRemovedIndex); 56 | if (linkedList.isEmpty()) { 57 | this.#table[index] = undefined; 58 | } 59 | return true; 60 | } 61 | } 62 | return false; // key not found 63 | } 64 | 65 | #elementToString(data) { 66 | if (typeof data === 'object' && data !== null) { 67 | return JSON.stringify(data); 68 | } else { 69 | return data.toString(); 70 | } 71 | } 72 | 73 | toString() { 74 | const keys = Object.keys(this.#table); 75 | let objString = `{${keys[0]} => ${this.#table[keys[0]].toString()}}`; 76 | for (let i = 1; i < keys.length; i++) { 77 | const value = this.#elementToString(this.#table[keys[i]]).toString(); 78 | objString = `${objString}\n{${keys[i]} => ${value}}`; 79 | } 80 | return objString; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/04-stack/__test__/stack.test.ts: -------------------------------------------------------------------------------- 1 | import {describe, expect, test, beforeEach} from '@jest/globals'; 2 | import Stack from '../stack'; 3 | 4 | describe('Stack', () => { 5 | let stack: Stack; 6 | 7 | beforeEach(() => { 8 | stack = new Stack(); 9 | }); 10 | 11 | test('push should add an item to the stack', () => { 12 | stack.push(1); 13 | stack.push(2); 14 | stack.push(3); 15 | 16 | expect(stack.size).toBe(3); 17 | }); 18 | 19 | test('pop should remove and return the top item from the stack', () => { 20 | stack.push(1); 21 | stack.push(2); 22 | stack.push(3); 23 | 24 | const poppedItem = stack.pop(); 25 | 26 | expect(poppedItem).toBe(3); 27 | expect(stack.size).toBe(2); 28 | }); 29 | 30 | test('peek should return the top item from the stack without removing it', () => { 31 | stack.push(1); 32 | stack.push(2); 33 | stack.push(3); 34 | 35 | const topItem = stack.peek(); 36 | 37 | expect(topItem).toBe(3); 38 | expect(stack.size).toBe(3); 39 | }); 40 | 41 | test('isEmpty should return true if the stack is empty', () => { 42 | expect(stack.isEmpty()).toBe(true); 43 | 44 | stack.push(1); 45 | 46 | expect(stack.isEmpty()).toBe(false); 47 | }); 48 | 49 | test('size should return the number of items in the stack', () => { 50 | expect(stack.size).toBe(0); 51 | 52 | stack.push(1); 53 | stack.push(2); 54 | stack.push(3); 55 | 56 | expect(stack.size).toBe(3); 57 | }); 58 | 59 | test('clear should remove all items from the stack', () => { 60 | stack.push(1); 61 | stack.push(2); 62 | stack.push(3); 63 | 64 | stack.clear(); 65 | 66 | expect(stack.size).toBe(0); 67 | expect(stack.isEmpty()).toBe(true); 68 | }); 69 | 70 | test('toString should return a string representation of the stack', () => { 71 | stack.push(1); 72 | stack.push(2); 73 | stack.push(3); 74 | 75 | expect(stack.toString()).toBe('1, 2, 3'); 76 | }); 77 | 78 | test('toString should return an empty string if the stack is empty', () => { 79 | expect(stack.toString()).toBe('Empty Stack'); 80 | }); 81 | 82 | test('toString should return a string representation of the stack with custom object', () => { 83 | const stack = new Stack<{key: string, value: number}>(); 84 | 85 | stack.push({key: 'a', value: 1}); 86 | stack.push({key: 'b', value: 2}); 87 | stack.push({key: 'c', value: 3}); 88 | 89 | expect(stack.toString()).toBe('{"key":"a","value":1}, {"key":"b","value":2}, {"key":"c","value":3}'); 90 | }); 91 | 92 | }); -------------------------------------------------------------------------------- /src/07-set/set.js: -------------------------------------------------------------------------------- 1 | class MySet { 2 | #items = {}; 3 | #size = 0; 4 | 5 | add(value) { 6 | if (!this.has(value)) { 7 | this.#items[value] = true; // mark the value as present 8 | this.#size++; 9 | return true; 10 | } 11 | return false; 12 | } 13 | 14 | addAll(values) { 15 | values.forEach(value => this.add(value)); 16 | } 17 | 18 | delete(value) { 19 | if (this.has(value)) { 20 | delete this.#items[value]; 21 | this.#size--; 22 | return true; 23 | } 24 | return false; 25 | } 26 | 27 | has(value) { 28 | return this.#items.hasOwnProperty(value); 29 | } 30 | 31 | values() { 32 | return Object.keys(this.#items); 33 | } 34 | 35 | get size() { 36 | return this.#size; 37 | } 38 | 39 | getSizeWithoutSizeProperty() { 40 | let count = 0; 41 | for (const key in this.#items) { 42 | if (this.#items.hasOwnProperty(key)) { 43 | count++; 44 | } 45 | } 46 | return count; 47 | } 48 | 49 | isEmpty() { 50 | return this.#size === 0; 51 | } 52 | 53 | clear() { 54 | this.#items = {}; 55 | this.#size = 0; 56 | } 57 | 58 | union(otherSet) { 59 | const unionSet = new MySet(); 60 | this.values().forEach(value => unionSet.add(value)); 61 | otherSet.values().forEach(value => unionSet.add(value)); 62 | return unionSet; 63 | } 64 | 65 | intersection(otherSet) { 66 | const intersectionSet = new MySet(); 67 | const [smallerSet, largerSet] = this.size <= otherSet.size ? [this, otherSet] : [otherSet, this]; 68 | smallerSet.values().forEach(value => { 69 | if (largerSet.has(value)) { 70 | intersectionSet.add(value); 71 | } 72 | }); 73 | return intersectionSet; 74 | } 75 | 76 | difference(otherSet) { 77 | const differenceSet = new MySet(); 78 | this.values().forEach(value => { 79 | if (!otherSet.has(value)) { 80 | differenceSet.add(value); 81 | } 82 | }); 83 | return differenceSet; 84 | } 85 | 86 | isSubsetOf(otherSet) { 87 | if (this.size > otherSet.size) { 88 | return false; 89 | } 90 | return this.values().every(value => otherSet.has(value)); 91 | } 92 | 93 | isSupersetOf(otherSet) { 94 | if (this.size < otherSet.size) { 95 | return false; 96 | } 97 | return otherSet.values().every(value => this.has(value)); 98 | } 99 | 100 | toString() { 101 | return this.values().join(', '); 102 | } 103 | } 104 | 105 | module.exports = MySet; 106 | -------------------------------------------------------------------------------- /src/05-queue-deque/leetcode/number-of-students-unable-to-eat-lunch.ts: -------------------------------------------------------------------------------- 1 | // Path: src/05-queue-deque/leetcode/number-of-students-unable-to-eat-lunch.ts 2 | // https://leetcode.com/problems/number-of-students-unable-to-eat-lunch/ 3 | 4 | // 1700. Number of Students Unable to Eat Lunch 5 | // Easy 6 | 7 | // hint 1: Simulate the process of giving sandwiches to students. 8 | // hint 2: Calculate those who will eat instead of those who will not. 9 | 10 | // Use two queues to simulate the process of giving sandwiches to students. 11 | 12 | function countStudents(students: number[], sandwiches: number[]): number { 13 | 14 | let count = 0; 15 | while(students.length > 0 && count < students.length) { 16 | if(students[0] === sandwiches[0]) { 17 | students.shift(); 18 | sandwiches.shift(); 19 | count = 0; 20 | } else { 21 | students.push(students.shift()); 22 | count++; 23 | } 24 | } 25 | 26 | return students.length; 27 | }; 28 | 29 | // Time complexity: O(n) 30 | // Space complexity: O(n) 31 | 32 | // Input: students = [1,1,0,0], sandwiches = [0,1,0,1] 33 | // Output: 0 34 | console.log(countStudents([1,1,0,0], [0,1,0,1])); // 0 35 | console.log(countStudents([1,1,1,0,0,1], [1,0,0,0,1,1])); // 3 36 | 37 | // optimized solution 38 | function countStudents2(students: number[], sandwiches: number[]): number { 39 | while (students.length > 0) { 40 | if (students[0] === sandwiches[0]) { 41 | students.shift(); 42 | sandwiches.shift(); 43 | } else { 44 | // check if there is a student who prefers the sandwich on the top of the stack to continue the loop 45 | if (students.includes(sandwiches[0])) { 46 | let num = students.shift(); 47 | students.push(num); 48 | } else { 49 | break; // if there is no student who prefers the sandwich on the top of the stack, break the loop 50 | } 51 | } 52 | } 53 | return students.length; 54 | }; 55 | 56 | // Time complexity: O(nˆ2) 57 | // Space complexity: O(1) 58 | 59 | function countStudentsOptimized(students, sandwiches) { 60 | const counts = [0, 0]; // Track counts of each preference (0 or 1) 61 | for (const student of students) { 62 | counts[student]++; 63 | } 64 | 65 | for (const sandwich of sandwiches) { 66 | if (counts[sandwich] === 0) { 67 | break; // No one wants this sandwich type anymore 68 | } 69 | counts[sandwich]--; 70 | } 71 | 72 | return counts[0] + counts[1]; // Remaining students who couldn't eat 73 | } 74 | -------------------------------------------------------------------------------- /src/03-array/06-other-methods.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/06-other-methods.ts 2 | 3 | // using Array.isArray() method 4 | console.log(typeof 'Learning Data Structures'); // string 5 | console.log(typeof 123); // number 6 | console.log(typeof { id: 1 }); // object 7 | console.log(typeof [1, 2, 3]); // object 8 | 9 | console.log(Array.isArray([1, 2, 3])); // true 10 | 11 | // real world example 12 | const jsonString = JSON.stringify('[{"id":1,"title":"The Fellowship of the Ring"},{"id":2,"title":"Fourth Wing"}]'); 13 | const dataReceived = JSON.parse(jsonString); 14 | 15 | if (Array.isArray(dataReceived)) { 16 | console.log('It is an array'); 17 | // check if The Fellowship of the Ring is in the array 18 | const fellowship = dataReceived.find((item) => { 19 | return item.title === 'The Fellowship of the Ring'; 20 | }); 21 | if (fellowship) { 22 | console.log('We received the book we were looking for!'); 23 | } else { 24 | console.log('We did not receive the book we were looking for!'); 25 | } 26 | } 27 | 28 | // using Array.from() method 29 | // @ts-ignore 30 | const numbers = [1, 2, 3, 4, 5]; 31 | const numbersCopy = Array.from(numbers); 32 | console.log(numbersCopy); // [1, 2, 3, 4, 5] 33 | 34 | const evens = Array.from(numbers, x => (x % 2 == 0)); 35 | console.log(evens); // [false, true, false, true, false] 36 | 37 | // Array.from() method creates a new, shallow-copied Array instance from an array-like or iterable object. 38 | // @ts-ignore 39 | const friends = [ 40 | { name: 'Frodo', age: 30 }, 41 | { name: 'Violet', age: 18 }, 42 | { name: 'Aelin', age: 20 } 43 | ]; 44 | const friendsCopy = Array.from(friends); 45 | console.log(friendsCopy); 46 | 47 | friends[0].name = 'Sam'; 48 | console.log(friendsCopy[0].name); // Sam 49 | 50 | // deep copy 51 | const friendsDeepCopy = JSON.parse(JSON.stringify(friends)); 52 | friends[0].name = 'Frodo'; 53 | console.log(friendsDeepCopy[0].name); // Sam 54 | 55 | // using Array.of() method 56 | const numbersArray = Array.of(1, 2, 3, 4, 5); 57 | console.log(numbersArray); // [1, 2, 3, 4, 5] 58 | 59 | let numbersCopy2 = Array.of(...numbersArray); 60 | 61 | // using Array.fill() method 62 | const tornamentResults = new Array(5).fill('pending'); 63 | 64 | tornamentResults.fill('win', 1, 3); 65 | console.log(tornamentResults); // ['pending', 'win', 'win', 'pending', 'pending'] 66 | 67 | // joining arrays 68 | const zero = 0; 69 | const positiveNumbers = [1, 2, 3]; 70 | const negativeNumbers = [-3, -2, -1]; 71 | let allNumbers = negativeNumbers.concat(zero, positiveNumbers); 72 | 73 | 74 | // to see the output of this file use the command: node src/03-array/06-other-methods.ts -------------------------------------------------------------------------------- /src/13-graph/02-using-bfs.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/02-using-bfs.js 2 | 3 | const Graph = require('./graph'); 4 | const { breadthFirstSearch, bfsShortestPath} = require('./bfs'); 5 | 6 | const airline = new Graph(); 7 | 8 | const airports = 'SEA SFO LAX LAS DEN DFW STL MDW JFK ATL MCO MIA'.split(' '); 9 | 10 | airports.forEach(airport => 11 | airline.addVertex(airport) 12 | ); 13 | 14 | airline.addEdge('SEA', 'SFO'); 15 | airline.addEdge('SEA', 'DEN'); 16 | airline.addEdge('SEA', 'MDW'); 17 | airline.addEdge('SFO', 'LAX'); 18 | airline.addEdge('LAX', 'LAS'); 19 | airline.addEdge('DEN', 'DFW'); 20 | airline.addEdge('DEN', 'STL'); 21 | airline.addEdge('STL', 'MDW'); 22 | airline.addEdge('STL', 'MCO'); 23 | airline.addEdge('MDW', 'ATL'); 24 | airline.addEdge('MDW', 'JFK'); 25 | airline.addEdge('ATL', 'JFK'); 26 | airline.addEdge('JFK', 'MCO'); 27 | airline.addEdge('JFK', 'MIA'); 28 | 29 | console.log(airline.toString()); 30 | 31 | // Output: 32 | // SEA -> SFO DEN MDW 33 | // SFO -> SEA LAX 34 | // LAX -> SFO LAS 35 | // LAS -> LAX 36 | // DEN -> SEA DFW STL 37 | // DFW -> DEN 38 | // STL -> DEN MDW MCO 39 | // MDW -> SEA STL ATL JFK 40 | // JFK -> MDW ATL MCO MIA 41 | // ATL -> MDW JFK 42 | // MCO -> STL JFK 43 | // MIA -> JFK 44 | 45 | console.log('--- BFS ---'); 46 | 47 | const printAirport = (value) => console.log('Visited airport: ' + value); 48 | 49 | breadthFirstSearch(airline,'SEA', printAirport); 50 | 51 | // Output: 52 | // --- BFS --- 53 | // Visited airport: SEA 54 | // Visited airport: SFO 55 | // Visited airport: DEN 56 | // Visited airport: MDW 57 | // Visited airport: LAX 58 | // Visited airport: DFW 59 | // Visited airport: STL 60 | // Visited airport: ATL 61 | // Visited airport: JFK 62 | // Visited airport: LAS 63 | // Visited airport: MCO 64 | // Visited airport: MIA 65 | 66 | console.log('--- BFS Shortest Path ---'); 67 | 68 | const shortestPath = bfsShortestPath(airline, 'SEA'); 69 | console.log(shortestPath); 70 | 71 | // { distances: { SEA: 0, SFO: 1, LAX: 2, LAS: 3, DEN: 1, DFW: 2, STL: 2, MDW: 1, JFK: 2, ATL: 2, MCO: 3, MIA: 3 }, 72 | // predecessors: { SEA: null, SFO: 'SEA', LAX: 'SFO', LAS: 'LAX', DEN: 'SEA', DFW: 'DEN', STL: 'DEN', MDW: 'SEA', JFK: 'MDW', ATL: 'MDW', MCO: 'STL', MIA: 'JFK'} 73 | // } 74 | 75 | // find the shortest path from SEA to JFK 76 | let path = []; 77 | for (let v = 'JFK'; v !== 'SEA'; v = shortestPath.predecessors[v]) { 78 | path.push(v); 79 | } 80 | path.push('SEA'); 81 | path.reverse(); 82 | 83 | console.log('Shortest path from SEA to JFK: ' + path.join(' -> ')); 84 | 85 | // Shortest path from SEA to JFK: SEA -> MDW -> JFK 86 | 87 | // to see the output of this file use the command: node src/13-graph/02-using-bfs.js -------------------------------------------------------------------------------- /src/03-array/05-transforming-array.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/05-transforming-array.js 2 | 3 | // @ts-ignore 4 | const numbers_ = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 5 | 6 | // using the map method 7 | const celsiusTemperatures = [0, 5, 10, 15, 20, 25, 30]; 8 | const fahrenheitTemperatures = celsiusTemperatures.map(celsius => { 9 | return (celsius * 9/5) + 32; 10 | }); 11 | console.log('Celsius:', celsiusTemperatures); // Celsius: [0, 5, 10, 15, 20, 25, 30] 12 | console.log('Fahrenheit:', fahrenheitTemperatures); // Fahrenheit: 32, 41, 50, 59, 68, 77, 86] 13 | 14 | 15 | // rewriting the above code using a loop 16 | const fahrenheitTemperaturesLoop = []; 17 | for (let i = 0; i < celsiusTemperatures.length; i++) { 18 | fahrenheitTemperaturesLoop.push((celsiusTemperatures[i] * 9/5) + 32); 19 | } 20 | 21 | // using the split method 22 | const namesFromCSV = 'Aelin,Gandalf,Violet,Poppy'; 23 | // @ts-ignore 24 | const names = namesFromCSV.split(','); 25 | console.log('Names:', names); // ['Aelin', 'Gandalf', 'Violet', 'Poppy'] 26 | 27 | // using the join method 28 | const namesCSV = names.join(';'); 29 | console.log('Names CSV:', namesCSV); // 'Aelin;Gandalf;Violet;Poppy' 30 | 31 | // using the reduce method 32 | const cartItems = [ 33 | { name: 'Laptop', price: 1200 }, 34 | { name: 'Smartphone', price: 500 }, 35 | { name: 'Headphones', price: 100 } 36 | ]; 37 | 38 | const totalPrice = cartItems.reduce((accumulator, currentItem) => { 39 | return accumulator + currentItem.price; 40 | }, 0); // Start the accumulator at 0 41 | console.log('Total Price:', totalPrice); // Total Price: 1800 42 | 43 | // rewriting the above code using a loop 44 | let totalPriceLoop = 0; 45 | for (let i = 0; i < cartItems.length; i++) { 46 | totalPriceLoop += cartItems[i].price; 47 | } 48 | 49 | // using the reduce method to find the maximum value 50 | const scores = [30, 70, 85, 90, 100]; 51 | const highestScore = scores.reduce((max, score) => { 52 | return score > max ? score : max; 53 | }); 54 | console.log('Highest Score:', highestScore); // Highest Score: 100 55 | 56 | // using with 57 | const leaderboard = [ 58 | { player: 'Poppy', score: 150 }, 59 | { player: 'Violet', score: 120 }, 60 | { player: 'Tory', score: 90 } 61 | ]; 62 | 63 | const updated = leaderboard.with(1, { player: 'Violet', score: 135 }); 64 | console.log('Leaderboard:', leaderboard); // [{ player: 'Poppy', score: 150 }, { player: 'Violet', score: 120 }, { player: 'Tory', score: 90 }] 65 | console.log('Updated :', updated); // [{ player: 'Poppy', score: 150 }, { player: 'Violet', score: 135 }, { player: 'Tory', score: 90 }] 66 | 67 | // to see the output of this file use the command: node src/03-array/05-transforming-array.js -------------------------------------------------------------------------------- /src/04-stack/leetcode/min-stack.ts: -------------------------------------------------------------------------------- 1 | // Path: src/04-stack/leetcode/min-stack.ts 2 | // https://leetcode.com/problems/min-stack/description/ 3 | 4 | class MinStack { 5 | stack: number[]; 6 | minStack: number[]; 7 | 8 | constructor() { 9 | this.stack = []; 10 | this.minStack = []; 11 | } 12 | 13 | push(x: number) { 14 | this.stack.push(x); 15 | if (this.minStack.length === 0 || x <= this.minStack[this.minStack.length - 1]) { 16 | this.minStack.push(x); 17 | } 18 | } 19 | 20 | pop() { 21 | const x = this.stack.pop(); 22 | if (x === this.minStack[this.minStack.length - 1]) { 23 | this.minStack.pop(); 24 | } 25 | } 26 | 27 | top() { 28 | return this.stack[this.stack.length - 1]; 29 | } 30 | 31 | getMin() { 32 | return this.minStack[this.minStack.length - 1]; 33 | } 34 | } 35 | 36 | // test 37 | const minStack = new MinStack(); 38 | minStack.push(-2); 39 | minStack.push(0); 40 | minStack.push(-3); 41 | console.log(minStack.getMin()); // -3 42 | minStack.pop(); 43 | console.log(minStack.top()); // 0 44 | console.log(minStack.getMin()); // -2 45 | 46 | // time complexity: O(1) 47 | // space complexity: O(n) 48 | 49 | // optimized solution 50 | class MinStack2 { 51 | stack: number[] = []; 52 | min: number = +Infinity; 53 | 54 | push(val: number): void { 55 | this.min = Math.min(val, this.min); 56 | this.stack.push(val); 57 | } 58 | 59 | pop(): void { 60 | const val = this.stack.pop(); 61 | if (this.min === val) this.min = Math.min(...this.stack); 62 | } 63 | 64 | top(): number { 65 | return this.stack[this.stack.length - 1]; 66 | } 67 | 68 | getMin(): number { 69 | return this.min; 70 | } 71 | } 72 | 73 | class MinStack3 { 74 | private stack: number[]; 75 | private minStack: number[]; 76 | 77 | constructor() { 78 | this.stack = []; 79 | this.minStack = []; 80 | } 81 | 82 | push(val: number): void { 83 | this.stack.push(val); 84 | 85 | if (this.minStack.length === 0) this.minStack.push(val); 86 | else { 87 | const currentMin = this.minStack[this.minStack.length - 1]; 88 | 89 | this.minStack.push(val < currentMin ? val : currentMin); 90 | } 91 | } 92 | 93 | pop(): void { 94 | this.stack.pop(); 95 | this.minStack.pop(); 96 | } 97 | 98 | top(): number { 99 | return this.stack[this.stack.length - 1]; 100 | } 101 | 102 | getMin(): number { 103 | return this.minStack[this.minStack.length - 1]; 104 | } 105 | } 106 | 107 | // to see the output of this file use the command: node src/04-stack/leetcode/min-stack.ts -------------------------------------------------------------------------------- /src/04-stack/leetcode/valid-parentheses.ts: -------------------------------------------------------------------------------- 1 | // Path: src/04-stack/leetcode/valid-parentheses.ts 2 | // https://leetcode.com/problems/valid-parentheses/description/ 3 | 4 | /** 5 | * @param {string} s 6 | * @return {boolean} 7 | */ 8 | const isValid = function(s) { 9 | const stack = []; 10 | const open = ['(', '[', '{']; 11 | const close = [')', ']', '}']; 12 | 13 | for (let i = 0; i < s.length; i++) { 14 | if (open.includes(s[i])) { 15 | stack.push(s[i]); 16 | } else if (close.includes(s[i])) { 17 | const last = stack.pop(); 18 | if (open.indexOf(last) !== close.indexOf(s[i])) { 19 | return false; 20 | } 21 | } 22 | } 23 | return stack.length === 0; 24 | } 25 | 26 | // test 27 | console.log(isValid('()')); // true 28 | console.log(isValid('()[]{}')); // true 29 | console.log(isValid('(]')); // false 30 | console.log(isValid('(')); // false 31 | console.log(isValid(')')); // false 32 | 33 | // time complexity: O(n) 34 | // space complexity: O(n) 35 | 36 | // rewrite the code using a map 37 | const isValid2 = function(s) { 38 | const stack = []; 39 | const map = { 40 | '(': ')', 41 | '[': ']', 42 | '{': '}' 43 | }; 44 | 45 | for (let i = 0; i < s.length; i++) { 46 | if (map[s[i]]) { 47 | stack.push(s[i]); 48 | } else if (s[i] !== map[stack.pop()]) { 49 | return false; 50 | } 51 | } 52 | return stack.length === 0; 53 | } 54 | 55 | // test 56 | console.log(isValid2('()')); // true 57 | console.log(isValid2('()[]{}')); // true 58 | console.log(isValid2('(]')); // false 59 | console.log(isValid2('(')); // false 60 | 61 | // time complexity: O(n) 62 | // space complexity: O(n) 63 | 64 | // optimize the code 65 | const isValid3 = function(s) { 66 | 67 | // optimization 1: if the length of the string is odd, return false 68 | if (s.length % 2 === 1) return false; 69 | 70 | // optimization 2: if the first character is a closing bracket, return false 71 | if (s[0] === ')' || s[0] === ']' || s[0] === '}') return false; 72 | 73 | // optimization 3: if the last character is an opening bracket, return false 74 | if (s[s.length - 1] === '(' || s[s.length - 1] === '[' || s[s.length - 1] === '{') return false; 75 | 76 | const stack = []; 77 | for (let i = 0; i < s.length; i++) { 78 | if (s[i] === '(' || s[i] === '[' || s[i] === '{') stack.push(s[i]) 79 | else { 80 | const top = stack.pop() 81 | if (top === '(' && s[i] !== ')') return false 82 | if (top === '[' && s[i] !== ']') return false 83 | if (top === '{' && s[i] !== '}') return false 84 | if (top === undefined) return false 85 | } 86 | } 87 | 88 | return stack.length === 0 89 | } 90 | 91 | // to see the output of this file use the command: node src/04-stack/leetcode/valid-parentheses.ts -------------------------------------------------------------------------------- /src/13-graph/dfs.js: -------------------------------------------------------------------------------- 1 | // src/13-graph/dfs.js 2 | 3 | const Colors = { 4 | WHITE: 0, 5 | GREY: 1, 6 | BLACK: 2 7 | }; 8 | 9 | const initializeColor = vertices => { 10 | const color = {}; 11 | vertices.forEach(vertex => { 12 | color[vertex] = Colors.WHITE; 13 | }); 14 | return color; 15 | } 16 | 17 | const depthFirstSearch = (graph, callback) => { 18 | const vertices = graph.vertices; 19 | const adjList = graph.adjList; 20 | const color = initializeColor(vertices); 21 | 22 | for (let i = 0; i < vertices.length; i++) { 23 | if (color[vertices[i]] === Colors.WHITE) { 24 | depthFirstSearchVisit(vertices[i], color, adjList, callback); 25 | } 26 | } 27 | } 28 | 29 | const depthFirstSearchVisit = (vertex, color, adjList, callback) => { 30 | color[vertex] = Colors.GREY; // Mark as discovered 31 | if (callback) { 32 | callback(vertex); 33 | } 34 | const neighbors = adjList.get(vertex); 35 | for (let i = 0; i < neighbors.length; i++) { 36 | const neighbor = neighbors[i]; 37 | if (color[neighbor] === Colors.WHITE) { // If unvisited - recursive call 38 | depthFirstSearchVisit(neighbor, color, adjList, callback); 39 | } 40 | } 41 | color[vertex] = Colors.BLACK; // Mark as explored 42 | } 43 | 44 | const enhancedDepthFirstSearch = (graph, callback) => { 45 | const vertices = graph.vertices; 46 | const adjList = graph.adjList; 47 | const color = initializeColor(vertices); 48 | const discovery = {}; // Discovery times 49 | const finished = {}; // Finish times 50 | const predecessors = {}; 51 | const time = { count: 0 }; 52 | 53 | for (let i = 0; i < vertices.length; i++) { 54 | finished[vertices[i]] = 0; 55 | discovery[vertices[i]] = 0; 56 | predecessors[vertices[i]] = null; 57 | } 58 | 59 | for (let i = 0; i < vertices.length; i++) { 60 | if (color[vertices[i]] === Colors.WHITE) { 61 | enhancedDFSVisit(vertices[i], color, discovery, finished, predecessors, time, adjList); 62 | } 63 | } 64 | 65 | return { discovery, finished, predecessors}; 66 | } 67 | 68 | const enhancedDFSVisit = (vertex, color, discovery, finished, predecessors, time, adjList) => { 69 | color[vertex] = Colors.GREY; 70 | discovery[vertex] = ++time.count; // Record discovery time 71 | const neighbors = adjList.get(vertex); 72 | for (let i = 0; i < neighbors.length; i++) { 73 | const neighbor = neighbors[i]; 74 | if (color[neighbor] === Colors.WHITE) { 75 | predecessors[neighbor] = vertex; // Record predecessor 76 | enhancedDFSVisit(neighbor, color, discovery, finished, predecessors, time, adjList); 77 | } 78 | } 79 | color[vertex] = Colors.BLACK; 80 | finished[vertex] = ++time.count; // Record finish time 81 | }; 82 | 83 | module.exports = { depthFirstSearch, enhancedDepthFirstSearch}; -------------------------------------------------------------------------------- /src/04-stack/decimal-to-base.ts: -------------------------------------------------------------------------------- 1 | // src/04-stack/decimal-to-base.js 2 | 3 | import Stack from './stack'; 4 | 5 | /** 6 | * Converts a decimal number to a given base 7 | * @param {number} decimalNumber - decimal number to be converted 8 | * @param {number} base - base to convert the decimal number to 9 | * @returns {string} base representation of the decimal number 10 | */ 11 | function decimalToBase(decimalNumber: number, base : number) { 12 | 13 | if (base < 2 || base > 36) { 14 | throw new Error('Base must be between 2 and 36'); 15 | } 16 | 17 | const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // Digits for base 36 18 | const remainderStack = new Stack(); 19 | let baseString = ''; 20 | 21 | while (decimalNumber > 0) { 22 | const remainder = Math.floor(decimalNumber % base); 23 | remainderStack.push(remainder); 24 | decimalNumber = Math.floor(decimalNumber / base); 25 | } 26 | 27 | while (!remainderStack.isEmpty()) { 28 | baseString += digits[remainderStack.pop()]; // Use digit mapping 29 | } 30 | 31 | return baseString; 32 | } 33 | 34 | /** 35 | * Converts a decimal number to a given base 36 | * @param {number} decimalNumber - decimal number to be converted (between 2 and 36 or 64) 37 | * @param {number} base - base to convert the decimal number to 38 | * @returns {string} base representation of the decimal number 39 | */ 40 | function decimalToBase64(decimalNumber: number, base: number) { 41 | if (base < 2 || (base > 36 && base !== 64)) { 42 | throw new Error('Base must be between 2 and 36 or 64'); 43 | } 44 | 45 | const digits = base === 64 ? 46 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' : 47 | '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 48 | 49 | const remainderStack = new Stack(); 50 | let baseString = ''; 51 | 52 | while (decimalNumber > 0) { 53 | const remainder = Math.floor(decimalNumber % base); 54 | remainderStack.push(remainder); 55 | decimalNumber = Math.floor(decimalNumber / base); 56 | } 57 | 58 | while (!remainderStack.isEmpty()) { 59 | baseString += digits[remainderStack.pop()]; 60 | } 61 | 62 | // Handle padding for Base64 (if necessary) 63 | /* Base64 encodes data in groups of 3 bytes (24 bits). 64 | Each group is represented by four Base64 characters (6 bits per character). 65 | If the input data length isn't divisible by 3, padding characters (=) are added to the end of the output 66 | to ensure that the total length is a multiple of 4. 67 | This is important for proper decoding of the Base64 string back to its original data. */ 68 | if (base === 64) { 69 | while (baseString.length % 4 !== 0) { 70 | baseString += '='; 71 | } 72 | } 73 | 74 | return baseString; 75 | } 76 | 77 | export { decimalToBase, decimalToBase64 }; -------------------------------------------------------------------------------- /src/03-array/03-iterator-functions.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/03-iterator-functions.js 2 | 3 | // using forEach method 4 | const raffleParticipants = ['poppy@email.com', 'sera@email.com', 'millie@example.com']; 5 | const sendWelcomeEmail = email => { 6 | console.log(`Welcome email sent to ${email}`); 7 | }; 8 | 9 | raffleParticipants.forEach(email => sendWelcomeEmail(email)); 10 | 11 | raffleParticipants.forEach(sendWelcomeEmail); 12 | 13 | const sendConfirmationEmail = (email, raffleNumber) => { 14 | console.log(`Hi ${email}, your raffle number is ${raffleNumber}`); 15 | }; 16 | 17 | const sendSpecialPrizeEmail = (email) => { 18 | console.log(`Congrats ${email}! You've won a special prize!`); 19 | }; 20 | 21 | raffleParticipants.forEach((value, index, array) => { 22 | const raffleNumber = index + 1; // Raffle numbers start from 1 23 | sendConfirmationEmail(value, raffleNumber); 24 | 25 | if (index === array.length - 1) { // Check if it's the last element 26 | sendSpecialPrizeEmail(value); 27 | } 28 | }); 29 | 30 | // using every method 31 | const formFields = [ 32 | { id: 'username', value: 'poppy' }, 33 | { id: 'email', value: 'poppy@email.com' }, 34 | { id: 'password', value: 'bookClub123!' }]; 35 | 36 | const isFormValid = () => 37 | formFields.every(field => field.value !== '' && field.value !== null); 38 | 39 | 40 | const onFormSubmit = () => { 41 | if (isFormValid()) { 42 | console.log('Form submitted successfully!'); 43 | } else { 44 | console.log('Please fill out all required fields.'); 45 | } 46 | } 47 | onFormSubmit(); // Form submitted successfully! 48 | 49 | // using a for loop 50 | const isFormValidForLoop = () => { 51 | for (let i = 0; i < formFields.length; i++) { 52 | if (formFields[i].value === '' || formFields[i].value === null) { 53 | return false; 54 | } 55 | } 56 | return true; 57 | }; 58 | 59 | 60 | // using some method 61 | const products = [ 62 | { name: 'Laptop', inStock: true }, 63 | { name: 'Smartphone', inStock: false }, 64 | { name: 'Headphones', inStock: true } 65 | ]; 66 | 67 | const searchQuery = 'phone'; 68 | const isProductAvailable = products.some(product => { 69 | return product.name.toLowerCase().includes(searchQuery.toLowerCase()) && product.inStock; 70 | }); 71 | 72 | console.log(isProductAvailable ? 'Product Available!' : 'No Products Found.'); // Product Available! 73 | 74 | // using a for loop 75 | const isProductAvailableForLoop = () => { 76 | for (let i = 0; i < products.length; i++) { 77 | if (products[i].name.toLowerCase().includes(searchQuery.toLowerCase()) && products[i].inStock) { 78 | return true; // Found a match, no need to continue checking 79 | } 80 | } 81 | return false; // No match found after checking all products 82 | }; 83 | 84 | 85 | // to see the output of this file use the command: node src/03-array/03-iterator-functions.js -------------------------------------------------------------------------------- /src/04-stack/decimal-to-base.js: -------------------------------------------------------------------------------- 1 | // src/04-stack/decimal-to-base.js 2 | 3 | const Stack = require('./stack'); 4 | 5 | /** 6 | * Converts a decimal number to a given base 7 | * @param {number} decimalNumber - decimal number to be converted 8 | * @param {number} base - base to convert the decimal number to 9 | * @returns {string} base representation of the decimal number 10 | */ 11 | function decimalToBase(decimalNumber, base) { 12 | 13 | if (base < 2 || base > 36) { 14 | throw new Error('Base must be between 2 and 36'); 15 | } 16 | 17 | if (decimalNumber === 0) { return '0'; } 18 | 19 | if (decimalNumber < 0) { 20 | throw new Error('Negative numbers are not supported'); 21 | } 22 | 23 | const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // Digits for base 36 24 | const remainderStack = new Stack(); 25 | let baseString = ''; 26 | 27 | while (decimalNumber > 0) { 28 | const remainder = Math.floor(decimalNumber % base); 29 | remainderStack.push(remainder); 30 | decimalNumber = Math.floor(decimalNumber / base); 31 | } 32 | 33 | while (!remainderStack.isEmpty()) { 34 | baseString += digits[remainderStack.pop()]; // Use digit mapping 35 | } 36 | 37 | return baseString; 38 | } 39 | 40 | /** 41 | * Converts a decimal number to a given base 42 | * @param {number} decimalNumber - decimal number to be converted (between 2 and 36 or 64) 43 | * @param {number} base - base to convert the decimal number to 44 | * @returns {string} base representation of the decimal number 45 | */ 46 | function decimalToBase64(decimalNumber, base) { 47 | if (base < 2 || (base > 36 && base !== 64)) { 48 | throw new Error('Base must be between 2 and 36 or 64'); 49 | } 50 | 51 | const digits = base === 64 ? 52 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' : 53 | '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 54 | 55 | const remainderStack = new Stack(); 56 | let baseString = ''; 57 | 58 | while (decimalNumber > 0) { 59 | const remainder = Math.floor(decimalNumber % base); 60 | remainderStack.push(remainder); 61 | decimalNumber = Math.floor(decimalNumber / base); 62 | } 63 | 64 | while (!remainderStack.isEmpty()) { 65 | baseString += digits[remainderStack.pop()]; 66 | } 67 | 68 | // Handle padding for Base64 (if necessary) 69 | /* Base64 encodes data in groups of 3 bytes (24 bits). 70 | Each group is represented by four Base64 characters (6 bits per character). 71 | If the input data length isn't divisible by 3, padding characters (=) are added to the end of the output 72 | to ensure that the total length is a multiple of 4. 73 | This is important for proper decoding of the Base64 string back to its original data. */ 74 | if (base === 64) { 75 | while (baseString.length % 4 !== 0) { 76 | baseString += '='; 77 | } 78 | } 79 | 80 | return baseString; 81 | } 82 | 83 | module.exports = { decimalToBase, decimalToBase64 }; -------------------------------------------------------------------------------- /src/10-tree/segment-tree.js: -------------------------------------------------------------------------------- 1 | 2 | class SegmentTree { 3 | #inputArray; 4 | #segmentTree; 5 | #operationFallback; 6 | 7 | constructor(inputArray, operationFallback) { 8 | this.#inputArray = inputArray; 9 | this.#operationFallback = operationFallback; 10 | this.#segmentTree = Array(inputArray.length * 4).fill(0); 11 | 12 | this.#build(0, 0, inputArray.length - 1); 13 | } 14 | 15 | #build(currentIndex, leftIndex, rightIndex) { 16 | if (leftIndex === rightIndex) { 17 | this.#segmentTree[currentIndex] = this.#inputArray[leftIndex]; 18 | return; 19 | } 20 | 21 | const middleIndex = Math.floor((leftIndex + rightIndex) / 2); 22 | const leftChildIndex = 2 * currentIndex + 1; 23 | const rightChildIndex = 2 * currentIndex + 2; 24 | 25 | this.#build(leftChildIndex, leftIndex, middleIndex); 26 | this.#build(rightChildIndex, middleIndex + 1, rightIndex); 27 | 28 | this.#segmentTree[currentIndex] = this.#operationFallback( 29 | this.#segmentTree[leftChildIndex], 30 | this.#segmentTree[rightChildIndex] 31 | ); 32 | } 33 | 34 | query(leftIndex, rightIndex) { 35 | return this.#query(0, 0, this.#inputArray.length - 1, leftIndex, rightIndex); 36 | } 37 | 38 | #query(currentIndex, leftIndex, rightIndex, queryLeftIndex, queryRightIndex) { 39 | if (queryLeftIndex > rightIndex || queryRightIndex < leftIndex) { 40 | return 0; 41 | } 42 | 43 | if (queryLeftIndex <= leftIndex && queryRightIndex >= rightIndex) { 44 | return this.#segmentTree[currentIndex]; 45 | } 46 | 47 | const middleIndex = Math.floor((leftIndex + rightIndex) / 2); 48 | const leftChildIndex = 2 * currentIndex + 1; 49 | const rightChildIndex = 2 * currentIndex + 2; 50 | 51 | return this.#operationFallback( 52 | this.#query(leftChildIndex, leftIndex, middleIndex, queryLeftIndex, queryRightIndex), 53 | this.#query(rightChildIndex, middleIndex + 1, rightIndex, queryLeftIndex, queryRightIndex) 54 | ); 55 | } 56 | 57 | update(index, value) { 58 | this.#update(0, 0, this.#inputArray.length - 1, index, value); 59 | } 60 | 61 | #update(currentIndex, leftIndex, rightIndex, index, value) { 62 | if (leftIndex === rightIndex) { 63 | this.#segmentTree[currentIndex] = value; 64 | return; 65 | } 66 | 67 | const middleIndex = Math.floor((leftIndex + rightIndex) / 2); 68 | const leftChildIndex = 2 * currentIndex + 1; 69 | const rightChildIndex = 2 * currentIndex + 2; 70 | 71 | if (index <= middleIndex) { 72 | this.#update(leftChildIndex, leftIndex, middleIndex, index, value); 73 | } else { 74 | this.#update(rightChildIndex, middleIndex + 1, rightIndex, index, value); 75 | } 76 | 77 | this.#segmentTree[currentIndex] = this.#operationFallback( 78 | this.#segmentTree[leftChildIndex], 79 | this.#segmentTree[rightChildIndex] 80 | ); 81 | } 82 | 83 | toString() { 84 | return this.#segmentTree.join(', '); 85 | } 86 | } -------------------------------------------------------------------------------- /src/05-queue-deque/__test__/deque.test.ts: -------------------------------------------------------------------------------- 1 | import {describe, expect, test, beforeEach} from '@jest/globals'; 2 | import Deque from '../deque'; 3 | 4 | describe('Deque', () => { 5 | let deque: Deque; 6 | 7 | beforeEach(() => { 8 | deque = new Deque(); 9 | }); 10 | 11 | test('should add an element to the front of the deque', () => { 12 | deque.addFront(1); 13 | deque.addFront(2); 14 | expect(deque.toString()).toBe('2, 1'); 15 | }); 16 | 17 | test('should add an element to the rear of the deque', () => { 18 | deque.addRear(1); 19 | deque.addRear(2); 20 | expect(deque.toString()).toBe('1, 2'); 21 | }); 22 | 23 | test('should remove an element from the front of the deque', () => { 24 | deque.addFront(1); 25 | deque.addFront(2); 26 | deque.removeFront(); 27 | expect(deque.toString()).toBe('1'); 28 | }); 29 | 30 | test('should return undefined when removing from an empty deque', () => { 31 | expect(deque.removeFront()).toBeUndefined(); 32 | }); 33 | 34 | test('should remove an element from the rear of the deque', () => { 35 | deque.addRear(1); 36 | deque.addRear(2); 37 | deque.removeRear(); 38 | expect(deque.toString()).toBe('1'); 39 | }); 40 | 41 | test('should return undefined when removing from an empty deque', () => { 42 | expect(deque.removeRear()).toBeUndefined(); 43 | }); 44 | 45 | test('should return the front element of the deque', () => { 46 | deque.addFront(1); 47 | deque.addFront(2); 48 | expect(deque.peekFront()).toBe(2); 49 | }); 50 | 51 | test('should return undefined when peeking from an empty deque', () => { 52 | expect(deque.peekFront()).toBeUndefined(); 53 | }); 54 | 55 | test('should return the rear element of the deque', () => { 56 | deque.addRear(1); 57 | deque.addRear(2); 58 | expect(deque.peekRear()).toBe(2); 59 | }); 60 | 61 | test('should return undefined when peeking from an empty deque', () => { 62 | expect(deque.peekRear()).toBeUndefined(); 63 | }); 64 | 65 | test('should return true if the deque is empty', () => { 66 | expect(deque.isEmpty()).toBe(true); 67 | }); 68 | 69 | test('should return the size of the deque', () => { 70 | deque.addFront(1); 71 | deque.addFront(2); 72 | expect(deque.size).toBe(2); 73 | }); 74 | 75 | test('should clear the deque', () => { 76 | deque.addFront(1); 77 | deque.addFront(2); 78 | deque.clear(); 79 | expect(deque.isEmpty()).toBe(true); 80 | }); 81 | 82 | test('should convert the deque to a string', () => { 83 | expect(deque.toString()).toBe('Empty Deque'); 84 | }); 85 | 86 | test('should convert the deque to a string with objects', () => { 87 | const deque = new Deque<{key: string, value: number}>(); 88 | 89 | deque.addFront({key: 'a', value: 1}); 90 | deque.addFront({key: 'b', value: 2}); 91 | deque.addFront({key: 'c', value: 3}); 92 | 93 | expect(deque.toString()).toBe('{"key":"c","value":3}, {"key":"b","value":2}, {"key":"a","value":1}'); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /src/03-array/11-array-chunking.js: -------------------------------------------------------------------------------- 1 | // src/03-array/11-array-chunking.js 2 | 3 | /** 4 | * Array Chunking - Split an array into chunks of a specified size 5 | * Common use case: Pagination, batch processing, data visualization 6 | */ 7 | 8 | // Approach 1: Using slice method 9 | function chunkArray(array, chunkSize) { 10 | if (chunkSize <= 0) { 11 | throw new Error('Chunk size must be greater than 0'); 12 | } 13 | 14 | const result = []; 15 | for (let i = 0; i < array.length; i += chunkSize) { 16 | result.push(array.slice(i, i + chunkSize)); 17 | } 18 | return result; 19 | } 20 | 21 | // Approach 2: Using splice method (modifies original array) 22 | function chunkArraySplice(array, chunkSize) { 23 | if (chunkSize <= 0) { 24 | throw new Error('Chunk size must be greater than 0'); 25 | } 26 | 27 | const result = []; 28 | const arrayCopy = [...array]; // Create a copy to avoid modifying original 29 | 30 | while (arrayCopy.length > 0) { 31 | result.push(arrayCopy.splice(0, chunkSize)); 32 | } 33 | return result; 34 | } 35 | 36 | // Approach 3: Using reduce method (functional approach) 37 | function chunkArrayReduce(array, chunkSize) { 38 | if (chunkSize <= 0) { 39 | throw new Error('Chunk size must be greater than 0'); 40 | } 41 | 42 | return array.reduce((chunks, item, index) => { 43 | const chunkIndex = Math.floor(index / chunkSize); 44 | 45 | if (!chunks[chunkIndex]) { 46 | chunks[chunkIndex] = []; // Start a new chunk 47 | } 48 | 49 | chunks[chunkIndex].push(item); 50 | return chunks; 51 | }, []); 52 | } 53 | 54 | // Examples 55 | console.log('=== Array Chunking Examples ===\n'); 56 | 57 | const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 58 | console.log('Original array:', numbers); 59 | 60 | console.log('\nChunk size 3 (slice method):', chunkArray(numbers, 3)); 61 | // Output: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]] 62 | 63 | console.log('Chunk size 4 (splice method):', chunkArraySplice(numbers, 4)); 64 | // Output: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]] 65 | 66 | console.log('Chunk size 2 (reduce method):', chunkArrayReduce(numbers, 2)); 67 | // Output: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] 68 | 69 | // Real-world example: Paginating data 70 | const users = [ 71 | 'Alice', 'Bob', 'Charlie', 'David', 'Eve', 72 | 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack' 73 | ]; 74 | 75 | const itemsPerPage = 3; 76 | const pages = chunkArray(users, itemsPerPage); 77 | 78 | console.log('\n=== Pagination Example ==='); 79 | console.log(`Total users: ${users.length}`); 80 | console.log(`Items per page: ${itemsPerPage}`); 81 | console.log(`Total pages: ${pages.length}\n`); 82 | 83 | pages.forEach((page, index) => { 84 | console.log(`Page ${index + 1}:`, page); 85 | }); 86 | 87 | // Edge cases 88 | console.log('\n=== Edge Cases ==='); 89 | console.log('Empty array:', chunkArray([], 3)); 90 | console.log('Chunk size larger than array:', chunkArray([1, 2], 5)); 91 | console.log('Chunk size of 1:', chunkArray([1, 2, 3], 1)); 92 | 93 | // to see the output of this file use the command: node src/03-array/11-array-chunking.js 94 | -------------------------------------------------------------------------------- /src/03-array/07-multidimensional-arrays.ts: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/07-multidimensional-arrays.ts 2 | 3 | let averageTempDay1 = [72, 75, 79, 79, 81, 81]; 4 | let averageTempDay2 = [81, 79, 75, 75, 73, 72]; 5 | 6 | // multidimensional array representation 7 | let averageTempMultipleDays = []; 8 | averageTempMultipleDays[0] = [72, 75, 79, 79, 81, 81]; 9 | averageTempMultipleDays[1] = [81, 79, 75, 75, 73, 73]; 10 | 11 | // same as above: 12 | averageTempMultipleDays = [ 13 | [72, 75, 79, 79, 81, 81], 14 | [81, 79, 75, 75, 73, 73] 15 | ]; 16 | 17 | // day 1 18 | averageTempMultipleDays[0] = []; 19 | averageTempMultipleDays[0][0] = 72; 20 | averageTempMultipleDays[0][1] = 75; 21 | averageTempMultipleDays[0][2] = 79; 22 | averageTempMultipleDays[0][3] = 79; 23 | averageTempMultipleDays[0][4] = 81; 24 | averageTempMultipleDays[0][5] = 81; 25 | // day 2 26 | averageTempMultipleDays[1] = []; 27 | averageTempMultipleDays[1][0] = 81; 28 | averageTempMultipleDays[1][1] = 79; 29 | averageTempMultipleDays[1][2] = 75; 30 | averageTempMultipleDays[1][3] = 75; 31 | averageTempMultipleDays[1][4] = 73; 32 | averageTempMultipleDays[1][5] = 73; 33 | 34 | // @ts-ignore 35 | function printMultidimensionalArray(myArray) { 36 | for (let i = 0; i < myArray.length; i++) { 37 | for (let j = 0; j < myArray[i].length; j++) { 38 | console.log(myArray[i][j]); 39 | } 40 | } 41 | } 42 | 43 | printMultidimensionalArray(averageTempMultipleDays); 44 | 45 | console.table(averageTempMultipleDays); 46 | 47 | //* * Multidimensional Matrix 48 | // Dimension 1 (i): each day 49 | // Dimension 2 (j): location 50 | // Dimension 3 (z): temperature 51 | // declare a 3-dimensional Array 3x3x3: 52 | let averageTempMultipleDaysAndLocation = []; 53 | 54 | // day 1 55 | averageTempMultipleDaysAndLocation[0] = []; 56 | averageTempMultipleDaysAndLocation[0][0] = [19, 20, 21]; // location 1 57 | averageTempMultipleDaysAndLocation[0][1] = [20, 22, 23]; // location 2 58 | averageTempMultipleDaysAndLocation[0][2] = [30, 31, 32]; // location 3 59 | 60 | // day 2 61 | averageTempMultipleDaysAndLocation[1] = []; 62 | averageTempMultipleDaysAndLocation[1][0] = [21, 22, 23]; // location 1 63 | averageTempMultipleDaysAndLocation[1][1] = [22, 23, 24]; // location 2 64 | averageTempMultipleDaysAndLocation[1][2] = [29, 30, 30]; // location 3 65 | 66 | // day 3 67 | averageTempMultipleDaysAndLocation[2] = []; 68 | averageTempMultipleDaysAndLocation[2][0] = [22, 23, 24]; // location 1 69 | averageTempMultipleDaysAndLocation[2][1] = [23, 24, 23]; // location 2 70 | averageTempMultipleDaysAndLocation[2][2] = [30, 31, 31]; // location 3 71 | 72 | 73 | function printMultidimensionalArray3D(myArray) { 74 | for (let i = 0; i < myArray.length; i++) { 75 | for (let j = 0; j < myArray[i].length; j++) { 76 | for (let z = 0; z < myArray[i][j].length; z++) { 77 | console.log(myArray[i][j][z]); 78 | } 79 | } 80 | } 81 | } 82 | 83 | printMultidimensionalArray3D(averageTempMultipleDaysAndLocation); 84 | 85 | console.table(averageTempMultipleDaysAndLocation); 86 | 87 | // to see the output of this file use the command: node src/03-array/07-multidimensional-arrays.ts -------------------------------------------------------------------------------- /src/03-array/06-other-methods.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/06-other-methods.js 2 | 3 | // using Array.isArray() method 4 | console.log(typeof 'Learning Data Structures'); // string 5 | console.log(typeof 123); // number 6 | console.log(typeof { id: 1 }); // object 7 | console.log(typeof [1, 2, 3]); // object 8 | 9 | console.log(Array.isArray([1, 2, 3])); // true 10 | console.log(Array.isArray({ id: 1 })); // false 11 | 12 | // real world example 13 | const jsonStringFromAPI = '[{"id":1,"title":"The Lord of the Rings"},{"id":2,"title":"The Hobbit"}]'; 14 | const dataReceived = JSON.parse(jsonStringFromAPI); 15 | 16 | if (Array.isArray(dataReceived)) { 17 | console.log('Received an array of books!'); 18 | // check if The Hobbit is in the array 19 | const hobbitBook = dataReceived.find(book => book.title === 'The Hobbit'); 20 | if (hobbitBook) { 21 | console.log('Found The Hobbit!'); 22 | } else { 23 | console.log('The Hobbit was not found in the results.'); 24 | } 25 | } else { 26 | console.log('Data received is not an array.'); 27 | // Handle other possible response types (single object, null, etc.) 28 | } 29 | // Received an array of books! 30 | 31 | // using Array.from() method 32 | const originalPixels = [255, 0, 0, 128, 128, 128]; // red, gray 33 | const backupPixels = Array.from(originalPixels); 34 | console.log(backupPixels); // [255, 0, 0, 128, 128, 128] 35 | 36 | // increase each pixel value by 50 37 | const brightenedPixels = Array.from(originalPixels, pixelValue => pixelValue + 50); 38 | console.log(brightenedPixels); // [305, 50, 50, 178, 178, 178] 39 | 40 | 41 | // Array.from() method creates a new, shallow-copied Array instance from an array-like or iterable object. 42 | let friends = [ 43 | { name: 'Frodo', age: 30 }, 44 | { name: 'Violet', age: 18 } 45 | ]; 46 | const friendsCopy = Array.from(friends); 47 | friends[0].name = 'Sam'; // modify the original 48 | console.log(friendsCopy[0].name); // 'Sam' 49 | 50 | 51 | // deep copy 52 | const anotherBackup = [...originalPixels]; 53 | const friendsDeepCopy = structuredClone(friends); 54 | friendsDeepCopy[0].name = 'Frodo'; 55 | console.log(friends[0].name); // 'Sam' - dit not change the original array 56 | 57 | 58 | // using Array.of() method 59 | let suits = Array.of('Hearts', 'Diamonds', 'Clubs', 'Spades'); 60 | console.log(suits); // ['Hearts', 'Diamonds', 'Clubs', 'Spades'] 61 | 62 | // same as: 63 | suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades']; 64 | 65 | const originalDeck = [ 1, 2, 3 ]; 66 | const shuffledDeck = Array.of(...originalDeck); 67 | 68 | // using Array.fill() method 69 | const tornamentResults = new Array(5).fill('pending'); 70 | 71 | tornamentResults.fill('win', 1, 3); 72 | console.log(tornamentResults); // ['pending', 'win', 'win', 'pending', 'pending'] 73 | 74 | // joining arrays 75 | const zero = 0; 76 | const positiveNumbers = [1, 2, 3]; 77 | const negativeNumbers = [-3, -2, -1]; 78 | let allNumbers = negativeNumbers.concat(zero, positiveNumbers); 79 | 80 | allNumbers = [...negativeNumbers,zero,...positiveNumbers]; 81 | 82 | // to see the output of this file use the command: node src/03-array/06-other-methods.js -------------------------------------------------------------------------------- /src/03-array/07-multidimensional-arrays.js: -------------------------------------------------------------------------------- 1 | // Path: src/03-array/07-multidimensional-arrays.js 2 | 3 | let averageHourlyTempDay1 = [72, 75, 79, 79, 81, 81]; 4 | let averageHourlyTempDay2 = [81, 79, 75, 75, 73, 72]; 5 | 6 | // multidimensional array representation 7 | let averageTempMultipleDays = []; 8 | averageTempMultipleDays[0] = [72, 75, 79, 79, 81, 81]; 9 | averageTempMultipleDays[1] = [81, 79, 75, 75, 73, 73]; 10 | 11 | // same as above: 12 | averageTempMultipleDays = [ 13 | [72, 75, 79, 79, 81, 81], 14 | [81, 79, 75, 75, 73, 73] 15 | ]; 16 | 17 | // day 1 18 | averageTempMultipleDays[0] = []; 19 | averageTempMultipleDays[0][0] = 72; 20 | averageTempMultipleDays[0][1] = 75; 21 | averageTempMultipleDays[0][2] = 79; 22 | averageTempMultipleDays[0][3] = 79; 23 | averageTempMultipleDays[0][4] = 81; 24 | averageTempMultipleDays[0][5] = 81; 25 | // day 2 26 | averageTempMultipleDays[1] = []; 27 | averageTempMultipleDays[1][0] = 81; 28 | averageTempMultipleDays[1][1] = 79; 29 | averageTempMultipleDays[1][2] = 75; 30 | averageTempMultipleDays[1][3] = 75; 31 | averageTempMultipleDays[1][4] = 73; 32 | averageTempMultipleDays[1][5] = 73; 33 | 34 | // @ts-ignore 35 | function printMultidimensionalArray(myArray) { 36 | for (let i = 0; i < myArray.length; i++) { 37 | for (let j = 0; j < myArray[i].length; j++) { 38 | console.log(myArray[i][j]); 39 | } 40 | } 41 | } 42 | 43 | printMultidimensionalArray(averageTempMultipleDays); 44 | 45 | console.table(averageTempMultipleDays); 46 | 47 | //* * Multidimensional Matrix 48 | // Dimension 1 (i): each day 49 | // Dimension 2 (j): location 50 | // Dimension 3 (z): temperature 51 | // declare a 3-dimensional Array 3x3x3: 52 | let averageTempMultipleDaysAndLocation = []; 53 | 54 | // day 1 55 | averageTempMultipleDaysAndLocation[0] = []; 56 | averageTempMultipleDaysAndLocation[0][0] = [19, 20, 21]; // location 1 57 | averageTempMultipleDaysAndLocation[0][1] = [20, 22, 23]; // location 2 58 | averageTempMultipleDaysAndLocation[0][2] = [30, 31, 32]; // location 3 59 | 60 | // day 2 61 | averageTempMultipleDaysAndLocation[1] = []; 62 | averageTempMultipleDaysAndLocation[1][0] = [21, 22, 23]; // location 1 63 | averageTempMultipleDaysAndLocation[1][1] = [22, 23, 24]; // location 2 64 | averageTempMultipleDaysAndLocation[1][2] = [29, 30, 30]; // location 3 65 | 66 | // day 3 67 | averageTempMultipleDaysAndLocation[2] = []; 68 | averageTempMultipleDaysAndLocation[2][0] = [22, 23, 24]; // location 1 69 | averageTempMultipleDaysAndLocation[2][1] = [23, 24, 23]; // location 2 70 | averageTempMultipleDaysAndLocation[2][2] = [30, 31, 31]; // location 3 71 | 72 | 73 | function printMultidimensionalArray3D(myArray) { 74 | for (let i = 0; i < myArray.length; i++) { 75 | for (let j = 0; j < myArray[i].length; j++) { 76 | for (let z = 0; z < myArray[i][j].length; z++) { 77 | console.log(myArray[i][j][z]); 78 | } 79 | } 80 | } 81 | } 82 | 83 | printMultidimensionalArray3D(averageTempMultipleDaysAndLocation); 84 | 85 | console.table(averageTempMultipleDaysAndLocation); 86 | 87 | // to see the output of this file use the command: node src/03-array/07-multidimensional-arrays.js -------------------------------------------------------------------------------- /src/03-array/11-array-chunking.ts: -------------------------------------------------------------------------------- 1 | // src/03-array/11-array-chunking.ts 2 | 3 | /** 4 | * Array Chunking - Split an array into chunks of a specified size 5 | * Common use case: Pagination, batch processing, data visualization 6 | */ 7 | 8 | // Approach 1: Using slice method 9 | function chunkArray(array: T[], chunkSize: number): T[][] { 10 | if (chunkSize <= 0) { 11 | throw new Error('Chunk size must be greater than 0'); 12 | } 13 | 14 | const result: T[][] = []; 15 | for (let i = 0; i < array.length; i += chunkSize) { 16 | result.push(array.slice(i, i + chunkSize)); 17 | } 18 | return result; 19 | } 20 | 21 | // Approach 2: Using splice method (modifies original array) 22 | function chunkArraySplice(array: T[], chunkSize: number): T[][] { 23 | if (chunkSize <= 0) { 24 | throw new Error('Chunk size must be greater than 0'); 25 | } 26 | 27 | const result: T[][] = []; 28 | const arrayCopy = [...array]; // Create a copy to avoid modifying original 29 | 30 | while (arrayCopy.length > 0) { 31 | result.push(arrayCopy.splice(0, chunkSize)); 32 | } 33 | return result; 34 | } 35 | 36 | // Approach 3: Using reduce method (functional approach) 37 | function chunkArrayReduce(array: T[], chunkSize: number): T[][] { 38 | if (chunkSize <= 0) { 39 | throw new Error('Chunk size must be greater than 0'); 40 | } 41 | 42 | return array.reduce((chunks: T[][], item: T, index: number) => { 43 | const chunkIndex = Math.floor(index / chunkSize); 44 | 45 | if (!chunks[chunkIndex]) { 46 | chunks[chunkIndex] = []; // Start a new chunk 47 | } 48 | 49 | chunks[chunkIndex].push(item); 50 | return chunks; 51 | }, []); 52 | } 53 | 54 | // Examples 55 | console.log('=== Array Chunking Examples ===\n'); 56 | 57 | const numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 58 | console.log('Original array:', numbers); 59 | 60 | console.log('\nChunk size 3 (slice method):', chunkArray(numbers, 3)); 61 | // Output: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]] 62 | 63 | console.log('Chunk size 4 (splice method):', chunkArraySplice(numbers, 4)); 64 | // Output: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]] 65 | 66 | console.log('Chunk size 2 (reduce method):', chunkArrayReduce(numbers, 2)); 67 | // Output: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] 68 | 69 | // Real-world example: Paginating data 70 | const users: string[] = [ 71 | 'Alice', 'Bob', 'Charlie', 'David', 'Eve', 72 | 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack' 73 | ]; 74 | 75 | const itemsPerPage = 3; 76 | const pages = chunkArray(users, itemsPerPage); 77 | 78 | console.log('\n=== Pagination Example ==='); 79 | console.log(`Total users: ${users.length}`); 80 | console.log(`Items per page: ${itemsPerPage}`); 81 | console.log(`Total pages: ${pages.length}\n`); 82 | 83 | pages.forEach((page, index) => { 84 | console.log(`Page ${index + 1}:`, page); 85 | }); 86 | 87 | // Edge cases 88 | console.log('\n=== Edge Cases ==='); 89 | console.log('Empty array:', chunkArray([], 3)); 90 | console.log('Chunk size larger than array:', chunkArray([1, 2], 5)); 91 | console.log('Chunk size of 1:', chunkArray([1, 2, 3], 1)); 92 | 93 | // to see the output of this file use the command: node src/03-array/11-array-chunking.ts 94 | -------------------------------------------------------------------------------- /src/11-heap/heap.js: -------------------------------------------------------------------------------- 1 | // src/11-heap/heap.js 2 | 3 | const Comparator = require('../10-tree/comparator'); 4 | 5 | class Heap { 6 | #heap = []; 7 | #compareFn; 8 | 9 | constructor(compareFn = Comparator.defaultCompareFn) { 10 | this.#compareFn = new Comparator(compareFn); 11 | } 12 | 13 | getLeftChildIndex(parentIndex) { 14 | return 2 * parentIndex + 1; 15 | } 16 | 17 | getRightChildIndex(parentIndex) { 18 | return 2 * parentIndex + 2; 19 | } 20 | 21 | getParentIndex(childIndex) { 22 | if (childIndex === 0) { return undefined; } 23 | return Math.floor((childIndex - 1) / 2); 24 | } 25 | 26 | insert(value) { 27 | if (value) { 28 | const index = this.#heap.length; 29 | this.#heap.push(value); 30 | this.#siftUp(index); 31 | return true; 32 | } 33 | return false; 34 | } 35 | 36 | #siftUp(index) { 37 | const parentIndex = this.getParentIndex(index); 38 | 39 | if (parentIndex !== undefined && parentIndex >= 0 && 40 | this.#compareFn.greaterThan(this.#heap[parentIndex], this.#heap[index]) 41 | ) { 42 | [this.#heap[parentIndex], this.#heap[index]] = [this.#heap[index], this.#heap[parentIndex]]; 43 | this.#siftUp(parentIndex); 44 | } 45 | } 46 | 47 | extract() { 48 | if (this.#heap.length === 0) { 49 | return undefined; 50 | } 51 | 52 | if (this.#heap.length === 1) { 53 | return this.#heap.shift(); 54 | } 55 | 56 | const root = this.#heap[0]; 57 | this.#heap[0] = this.#heap.pop(); 58 | this.#siftDown(0); 59 | return root; 60 | } 61 | 62 | #siftDown(index) { 63 | const leftIndex = this.getLeftChildIndex(index); 64 | const rightIndex = this.getRightChildIndex(index); 65 | let smallerIndex = index; 66 | 67 | if (leftIndex < this.#heap.length && 68 | this.#compareFn.lessThan(this.#heap[leftIndex], this.#heap[smallerIndex]) 69 | ) { 70 | smallerIndex = leftIndex; 71 | } 72 | 73 | if (rightIndex < this.#heap.length && 74 | this.#compareFn.lessThan(this.#heap[rightIndex], this.#heap[smallerIndex]) 75 | ) { 76 | smallerIndex = rightIndex; 77 | } 78 | 79 | if (smallerIndex !== index) { 80 | [this.#heap[index], this.#heap[smallerIndex]] = [this.#heap[smallerIndex], this.#heap[index]]; 81 | this.#siftDown(smallerIndex); 82 | } 83 | } 84 | 85 | heapify(array) { 86 | this.#heap = array; 87 | const lastParentIndex = this.getParentIndex(this.#heap.length - 1); 88 | if (lastParentIndex !== undefined) { 89 | for (let i = lastParentIndex; i >= 0; i--) { 90 | this.#siftDown(i); 91 | } 92 | } 93 | } 94 | 95 | peek() { 96 | return this.#heap.length === 0 ? undefined : this.#heap[0]; 97 | } 98 | 99 | get size() { 100 | return this.#heap.length; 101 | } 102 | 103 | isEmpty() { 104 | return this.#heap.length === 0; 105 | } 106 | 107 | toArray() { 108 | return this.#heap.slice(); 109 | } 110 | 111 | clear() { 112 | this.#heap = []; 113 | } 114 | 115 | toString() { 116 | return this.#heap.toString(); 117 | } 118 | } 119 | 120 | module.exports = Heap; -------------------------------------------------------------------------------- /src/08-dictionary-hash/hash-table-linear-probing.js: -------------------------------------------------------------------------------- 1 | // src/08-dictionary-hash/hash-table-linear-probing.js 2 | 3 | class HashTableLinearProbing { 4 | #table = []; 5 | 6 | #loseLoseHashCode(key) { 7 | if (typeof key !== 'string') { 8 | key = this.#elementToString(key); 9 | } 10 | const calcASCIIValue = (acc, char) => acc + char.charCodeAt(0); 11 | const hash = key.split('').reduce((acc, char) => calcASCIIValue, 0); 12 | return hash % 37; // mod to reduce the hash code 13 | } 14 | 15 | #djb2HashCode(key) { 16 | if (typeof key !== 'string') { 17 | key = this.#elementToString(key); 18 | } 19 | const calcASCIIValue = (acc, char) => (acc * 33) + char.charCodeAt(0); 20 | const hash = key.split('').reduce((acc, char) => calcASCIIValue, 5381); 21 | return hash % 1013; 22 | } 23 | 24 | hash(key) { 25 | return this.#loseLoseHashCode(key); 26 | } 27 | 28 | #elementToString(data) { 29 | if (typeof data === 'object' && data !== null) { 30 | return JSON.stringify(data); 31 | } else { 32 | return data.toString(); 33 | } 34 | } 35 | 36 | put(key, value) { 37 | if (key != null && value != null) { 38 | let index = this.hash(key); 39 | 40 | // linear probing to find an empty slot 41 | while (this.#table[index] != null) { 42 | if (this.#table[index].key === key) { 43 | this.#table[index].value = value; // update existing key 44 | return true; 45 | } 46 | index++; 47 | index %= this.#table.length; // wrap around if end is reached 48 | } 49 | 50 | this.#table[index] = {key, value}; 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | get(key) { 57 | let index = this.hash(key); 58 | 59 | // Linear probing to search for the key 60 | while (this.#table[index] != null) { 61 | if (this.#table[index].key === key) { 62 | return this.#table[index].value; 63 | } 64 | index++; 65 | index %= this.#table.length; 66 | } 67 | 68 | return undefined; // key not found 69 | } 70 | 71 | remove(key) { 72 | let index = this.hash(key); 73 | while (this.#table[index] != null) { 74 | if (this.#table[index].key === key) { 75 | delete this.#table[index]; 76 | this.#verifyRemoveSideEffect(key, index); 77 | return true; 78 | } 79 | index++; 80 | index %= this.#table.length; 81 | } 82 | return false; // key not found 83 | } 84 | 85 | #verifyRemoveSideEffect(key, removedPosition) { 86 | const size = this.#table.length; 87 | let index = removedPosition + 1; 88 | while (this.#table[index] != null) { 89 | const currentKey = this.#table[index].key; 90 | const currentHash = this.hash(currentKey); 91 | // check if the element should be repositioned 92 | if (currentHash <= removedPosition) { 93 | this.#table[removedPosition] = this.#table[index]; 94 | delete this.#table[index]; 95 | removedPosition = index; 96 | } 97 | index++; 98 | index %= size; // Wrap around if end is reached 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/11-heap/leetcode/relative-ranks.ts: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/relative-ranks/description/ 2 | // 506. Relative Ranks 3 | 4 | function findRelativeRanks(nums: number[]): string[] { 5 | const sorted = [...nums].sort((a, b) => b - a); 6 | const map = new Map(); 7 | for (let i = 0; i < sorted.length; i++) { 8 | if (i === 0) { 9 | map.set(sorted[i], 'Gold Medal'); 10 | } else if (i === 1) { 11 | map.set(sorted[i], 'Silver Medal'); 12 | } else if (i === 2) { 13 | map.set(sorted[i], 'Bronze Medal'); 14 | } else { 15 | map.set(sorted[i], (i + 1).toString()); 16 | } 17 | } 18 | 19 | return nums.map((num) => map.get(num)!); 20 | } 21 | 22 | // console.log(findRelativeRanks([5, 4, 3, 2, 1])); // ["Gold Medal","Silver Medal","Bronze Medal","4","5"] 23 | // console.log(findRelativeRanks([10, 3, 8, 9, 4])); // ["Gold Medal","5","Bronze Medal","Silver Medal","4"] 24 | // console.log(findRelativeRanks([1])); // ["Gold Medal"] 25 | // console.log(findRelativeRanks([1, 2])); // ["Silver Medal","Gold Medal"] 26 | // console.log(findRelativeRanks([1, 2, 3])); // ["Bronze Medal","Silver Medal","Gold Medal"] 27 | 28 | function siftDown (array: number[], heapSize: number, rootIndex: number) { 29 | let largest = rootIndex; 30 | let left = 2 * rootIndex + 1; 31 | let right = 2 * rootIndex + 2; 32 | 33 | if (left < heapSize && array[left] < array[largest]) { 34 | largest = left; 35 | } 36 | 37 | if (right < heapSize && array[right] < array[largest]) { 38 | largest = right; 39 | } 40 | 41 | if (largest !== rootIndex) { 42 | [array[rootIndex], array[largest]] = [array[largest], array[rootIndex]]; 43 | siftDown(array, heapSize, largest); 44 | } 45 | }; 46 | 47 | 48 | function buildMaxHeap (array: number[]) { 49 | const heapSize = array.length; 50 | for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) { 51 | siftDown(array, heapSize, i); 52 | } 53 | } 54 | 55 | function heapSort(array: number[]) { 56 | buildMaxHeap(array); 57 | for (let i = array.length - 1; i > 0; i--) { 58 | [array[0], array[i]] = [array[i], array[0]]; 59 | siftDown(array, i, 0); 60 | } 61 | } 62 | 63 | // rewrite the solution using heap 64 | function findRelativeRanksHeap(nums: number[]): string[] { 65 | 66 | const sorted = [...nums]; 67 | heapSort(sorted); 68 | const map = new Map(); 69 | for (let i = 0; i < sorted.length; i++) { 70 | if (i === 0) { 71 | map.set(sorted[i], 'Gold Medal'); 72 | } else if (i === 1) { 73 | map.set(sorted[i], 'Silver Medal'); 74 | } else if (i === 2) { 75 | map.set(sorted[i], 'Bronze Medal'); 76 | } else { 77 | map.set(sorted[i], (i + 1).toString()); 78 | } 79 | } 80 | 81 | return nums.map((num) => map.get(num)!); 82 | } 83 | 84 | console.log(findRelativeRanksHeap([5, 4, 3, 2, 1])); // ["Gold Medal","Silver Medal","Bronze Medal","4","5"] 85 | console.log(findRelativeRanksHeap([10, 3, 8, 9, 4])); // ["Gold Medal","5","Bronze Medal","Silver Medal","4"] 86 | console.log(findRelativeRanksHeap([1])); // ["Gold Medal"] 87 | console.log(findRelativeRanksHeap([1, 2])); // ["Silver Medal","Gold Medal"] 88 | console.log(findRelativeRanksHeap([1, 2, 3])); // ["Bronze Medal","Silver Medal","Gold Medal"] 89 | 90 | // to see the output of this file use the command: npx ts-node src/11-heap/leetcode/relative-ranks.ts --------------------------------------------------------------------------------