├── .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
--------------------------------------------------------------------------------