Repository: CoffeelessProgrammer/Data-Structures-and-Algorithms-TS Branch: master Commit: 9eb4902adfab Files: 60 Total size: 120.8 KB Directory structure: gitextract_i53d2ydn/ ├── .gitignore ├── Algorithms/ │ ├── DynamicProgramming/ │ │ ├── Basics.ts │ │ ├── Fibonacci.ts │ │ └── Knapsack.ts │ ├── README.md │ ├── Recursion/ │ │ ├── Basics.ts │ │ ├── Factorial.ts │ │ └── Fibonacci.ts │ ├── Searching/ │ │ ├── Basics.ts │ │ ├── BreadthFirstTraversal.ts │ │ ├── DepthFirstTraversals.ts │ │ └── Exercise_BFSDFSUseCases.md │ └── Sorting/ │ ├── Basics.ts │ ├── BubbleSort.ts │ ├── Data/ │ │ └── PlantFamilies.ts │ ├── Exercise_SortingUseCases.md │ ├── InsertionSort.ts │ ├── MergeSort.ts │ ├── QuickSort.ts │ └── SelectionSort.ts ├── Big-O/ │ ├── Ex1.ts │ ├── Ex2.ts │ ├── Hello_Big_O.ts │ ├── README.md │ ├── Rule3.ts │ └── n_squared.ts ├── Data-Structures/ │ ├── Arrays/ │ │ ├── Basics.ts │ │ └── MyArray.ts │ ├── Graphs/ │ │ └── SimpleGraph.ts │ ├── Hash-Tables/ │ │ └── HashTable.ts │ ├── Linked-Lists/ │ │ ├── DoublyLinkedList.ts │ │ ├── DoublyNode.ts │ │ ├── LinkedList.ts │ │ └── SinglyNode.ts │ ├── README.md │ ├── Sequential/ │ │ ├── Queue.ts │ │ ├── Stack.ts │ │ └── StackLL.ts │ └── Trees/ │ ├── BinarySearchTree.test.ts │ ├── BinarySearchTree.ts │ └── BinaryTreeNode.ts ├── Playground/ │ ├── Demos/ │ │ ├── Classes_101.ts │ │ └── Objects_101.ts │ ├── Interviews/ │ │ └── HealthcareHM.ts │ ├── Puzzles/ │ │ ├── AngryFrogs.test.ts │ │ └── AngryFrogs.ts │ └── ZTM Challenges/ │ ├── Arrays/ │ │ ├── Compare_Arrays.test.ts │ │ ├── Compare_Arrays.ts │ │ ├── Merge_Sorted_Arrays.ts │ │ ├── Reverse_String.ts │ │ └── Sum_Pair.ts │ ├── Hash-Tables/ │ │ ├── Recurring_Symbol.test.ts │ │ └── Recurring_Symbol.ts │ ├── Josephus.ts │ ├── Recursion/ │ │ └── ReverseString.ts │ ├── StackifiedQueue.ts │ └── ValidatorBST.ts ├── README.md ├── deps.ts └── test_deps.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ deno_dir ================================================ FILE: Algorithms/DynamicProgramming/Basics.ts ================================================ // ----------------------- Class Implementation ----------------------- class MemoizationExample { private static cache = Object.create({}); public static resetCache() { this.cache = null; this.cache = Object.create({}); } public static memoizedAddTo42(n: number, visualize?: boolean): number { if (n in this.cache) { if (visualize) console.log('Found!', n, '+ 42 =', this.cache[n]); return this.cache[n]; } else { if (visualize) console.log('New encounter!', n); this.cache[n] = n + 42; return this.cache[n] } } public static addTo42(n: number): number { console.log('Long time!', n); return n + 42; } } // ----------------------- Closure Implementation ----------------------- function memoizedAddTo64() { let cache = Object.create({}); return function(n: number): number { if (n in cache) { console.log('Found!', n, '+ 64 =', cache[n]); return cache[n]; } else { console.log('New encounter!', n); cache[n] = n + 64; return cache[n] } } } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { console.log('------------ Memoization via Class ------------'); MemoizationExample.memoizedAddTo42(17, true); MemoizationExample.memoizedAddTo42(15, true); MemoizationExample.memoizedAddTo42(17, true); MemoizationExample.memoizedAddTo42(28, true); MemoizationExample.memoizedAddTo42(28, true); console.log('\n----------- Memoization via Closure -----------'); const memoized = memoizedAddTo64(); memoized(7); memoized(5); memoized(5); // RUN: deno run Algorithms/DynamicProgramming/Basics.ts } // --------------------------- Terminal Output: --------------------------- // ------------ Memoization via Class ------------ // New encounter! 17 // New encounter! 15 // Found! 17 + 42 = 59 // New encounter! 28 // Found! 28 + 42 = 70 // ----------- Memoization via Closure ----------- // New encounter! 7 // New encounter! 5 // Found! 5 + 64 = 69 ================================================ FILE: Algorithms/DynamicProgramming/Fibonacci.ts ================================================ // ---------------- Fibonacci Sequence ---------------- // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144... class FibonacciMemoized { private static cache = Object.create({}); private static _initialize = (() => { FibonacciMemoized.cache[1] = 0; FibonacciMemoized.cache[2] = 1; })(); public static nthTerm(nth: number): number { if (nth in this.cache) return this.cache[nth]; else this.cache[nth] = this.nthTerm(nth-2) + this.nthTerm(nth-1); return this.cache[nth]; } } function fibonacciBottomUp() { let fibonacciSequence: number[] = [0,1]; return function(nth: number): number | undefined { if (nth < 1) return undefined; else nth = Math.floor(nth); for (let i=fibonacciSequence.length; i <= nth; ++i) fibonacciSequence.push(fibonacciSequence[i-2]+ fibonacciSequence[i-1]); return fibonacciSequence[nth-1]; } } function executionTime(n: number): string { const t0 = performance.now(); console.log('Term', n+':', FibonacciMemoized.nthTerm(n)); const t1 = performance.now(); return (t1-t0) + 'ms'; } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { console.log('\n------------ Memoization ------------'); const a1 = Object.create({}); a1.run_1 = executionTime(256); a1.run_2 = executionTime(256); a1.run_3 = executionTime(16); a1.run_4 = executionTime(8); console.table(a1); console.log('\n------------ Bottom Up ------------'); const fibBot = fibonacciBottomUp(); console.log('Term 1:', fibBot(1)); console.log('Term 6:', fibBot(6)); console.log('Term 11:', fibBot(11)); console.log('Term 42:', fibBot(42)); // RUN: deno run Algorithms/DynamicProgramming/Fibonacci.ts } // --------------------------- Terminal Output: --------------------------- // // ------------ Memoization ------------ // Term 256: 8.757159534301882e+52 // Term 256: 8.757159534301882e+52 // Term 16: 610 // Term 8: 13 // ┌───────┬────────┐ // │ (idx) │ Values │ // ├───────┼────────┤ // │ run_1 │ "2ms" │ // │ run_2 │ "8ms" │ // │ run_3 │ "2ms" │ // │ run_4 │ "12ms" │ // └───────┴────────┘ // // ------------ Bottom Up ------------ // Term 1: 0 // Term 6: 5 // Term 11: 55 // Term 42: 165580141 ================================================ FILE: Algorithms/DynamicProgramming/Knapsack.ts ================================================ //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { // RUN: deno run Algorithms/DynamicProgramming/Knapsack.ts } ================================================ FILE: Algorithms/README.md ================================================ # Algorithms ## Core - [X] Recursion - [X] Dynamic Programming - [X] Comparison Sorting - [X] *Merge Sort* - [X] *Quicksort* - [X] Bubble Sort - [X] Selection Sort - [X] Insertion Sort - [X] Searching - [X] Linear Search - [ ] Binary Search - [X] Breadth First Search (BFS) - [X] Depth First Search (DFS) ## Resources - [Visualizing Data Structures & Algorithms](https://visualgo.net/en) - [Unicode Characters | RapidTables](https://www.rapidtables.com/code/text/unicode-characters.html) - [The Big-O Algorithm Complexity Cheat Sheet](https://www.bigocheatsheet.com/ "Big O Cheat Sheet") ### Dynamic Programming - [Closures | JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) ### Recursion - [Recursion | Brilliant.org](https://brilliant.org/wiki/recursion-problem-solving/) - [Tail Call Optimization in ES6](https://2ality.com/2015/06/tail-call-optimization.html) ### Searching - [Graph Traversals | VisuAlgo](https://visualgo.net/en/dfsbfs) - [Tree Traversals: Preorder, Inorder, & Postorder](https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/) - [Space Complexity: BFS vs DFS](https://stackoverflow.com/questions/9844193/what-is-the-time-and-space-complexity-of-a-breadth-first-and-depth-first-tree-tr) ### Sorting - [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) - [Animated Sorting | Toptal](https://www.toptal.com/developers/sorting-algorithms) - [Dancing Algorithms | AlgoRythmics](https://www.youtube.com/user/AlgoRythmics/videos) - [QuickSort vs Heapsort](https://stackoverflow.com/questions/2467751/quicksort-vs-heapsort) - [Importance of Stability in Sorting](https://stackoverflow.com/questions/1517793/what-is-stability-in-sorting-algorithms-and-why-is-it-important) ### Most Common Sorts - [Merge Sort | Brilliant.org](https://brilliant.org/wiki/merge/) - [Quicksort | Brilliant.org](https://brilliant.org/wiki/quick-sort/) - [Heap Sort | Brilliant.org](https://brilliant.org/wiki/heap-sort/) - [Radix Sort | Brilliant.org](https://brilliant.org/wiki/radix-sort/ "Non-Comparison Sort") - [Radix Sort Visualization](https://www.cs.usfca.edu/~galles/visualization/RadixSort.html) - [Counting Sort | Brilliant.org](https://brilliant.org/wiki/counting-sort/ "Non-Comparison Sort") - [Counting Sort Visualization](https://www.cs.usfca.edu/~galles/visualization/CountingSort.html) ================================================ FILE: Algorithms/Recursion/Basics.ts ================================================ function inception(repeat: number): string { // 1. Base Case(s) if (repeat === 0) return 'Done!\n'; // Base Case(s) must be written first in method // 2?. Input Validation if (repeat < 1) return 'Too small!\n'; // Input validation should occur after base case(s) console.log('Counter:', repeat); // 3. Recursive Call return inception(repeat-1); // The recursive call should return itself so the calculated value can bubble up } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { console.log(inception(5)); console.log(inception(1)); console.log(inception(-1)); // RUN: deno run Algorithms/Recursion/Basics.ts } // --------------------------- Terminal Output: --------------------------- // Counter: 5 // Counter: 4 // Counter: 3 // Counter: 2 // Counter: 1 // Done! // // Counter: 1 // Done! // // Too small! ================================================ FILE: Algorithms/Recursion/Factorial.ts ================================================ function calcFactorialRecursive(input: number): number { if (input <= 1) return 1; return input * calcFactorialRecursive(input-1); } function calcFactorialIterative(input: number): number | undefined { if (input < 0) return undefined; let factorial = 1; for (let i=2; i <= input; ++i) { factorial *= i; } return factorial; } function validateFactorialInput(input: number): boolean { return input>=0; } function printIterativeFactorial(input: number) { if (!validateFactorialInput(input)) { console.log("Input", input, "is invalid -_-'"); return; } console.log('Iterative Factorial', input+':', calcFactorialIterative(input)); } function printRecursiveFactorial(input: number) { if (!validateFactorialInput(input)) { console.log("Input", input, "is invalid -_-'"); return; } console.log('Recursive Factorial', input+':', calcFactorialRecursive(input)); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { printIterativeFactorial(0); printIterativeFactorial(1); printIterativeFactorial(2); printIterativeFactorial(3); printIterativeFactorial(5); printRecursiveFactorial(4); printRecursiveFactorial(0); printRecursiveFactorial(-1); printRecursiveFactorial(170); printIterativeFactorial(171); // RUN: deno run Algorithms/Recursion/Factorial.ts } // --------------------------- Terminal Output: --------------------------- // Iterative Factorial 0: 1 // Iterative Factorial 1: 1 // Iterative Factorial 2: 2 // Iterative Factorial 3: 6 // Iterative Factorial 5: 120 // Recursive Factorial 4: 24 // Recursive Factorial 0: 1 // Input -1 is invalid -_-' // Recursive Factorial 170: 7.257415615307994e+306 // Iterative Factorial 171: Infinity ================================================ FILE: Algorithms/Recursion/Fibonacci.ts ================================================ // ---------------- Fibonacci Sequence ---------------- // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144... // Time Complexity: O(2^n) Space Complexity: O(2^n) function fibonacciRecursive(nthElement: number): number | any { // Base Cases if (nthElement === 1) return 0; if (nthElement === 2) return 1; //Input Validation if (nthElement < 1) return undefined; // Recursive Call: The nth element equals the sum of the previous 2 elements. return fibonacciRecursive(nthElement-2) + fibonacciRecursive(nthElement-1); } // Time Complexity: O(n) Space Complexity: O(1) function fibonacciIterative(nthElement: number): number | undefined { if (nthElement < 1) return undefined; else nthElement = Math.floor(nthElement); let previous = 0; let current = 1; if (nthElement === 1) return previous; if (nthElement === 2) return current; let fibonacci: number = 0; for (let i = 3; i <= nthElement; ++i) { fibonacci = previous + current; previous = current; current = fibonacci; } return fibonacci; } function validateFibonacciInput(nthElement: number) { return nthElement>0; } function printFibonacciIterative(nthElement: number) { console.log("Fibonacci Iterative", nthElement+':', fibonacciIterative(nthElement)); } function printFibonacciRecursive(nthElement: number) { if(!validateFibonacciInput(nthElement)) { console.log("Input", nthElement, "is invalid -_-'"); return; } console.log("Fibonacci Recursive", nthElement+':', fibonacciRecursive(nthElement)); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { console.log('------------ Iterative ------------'); printFibonacciIterative(0); printFibonacciIterative(1); printFibonacciIterative(2); printFibonacciIterative(3); printFibonacciIterative(4); printFibonacciIterative(5); printFibonacciIterative(6); printFibonacciIterative(9); printFibonacciIterative(42); printFibonacciIterative(50); printFibonacciIterative(100); console.log('\n------------ Recursive ------------'); printFibonacciRecursive(-1); printFibonacciRecursive(0); printFibonacciRecursive(1); printFibonacciRecursive(2); printFibonacciRecursive(3); printFibonacciRecursive(4); printFibonacciRecursive(5); printFibonacciRecursive(7); printFibonacciRecursive(8); printFibonacciRecursive(42); // Notice how much longer this takes than the iterative version // RUN: deno run Algorithms/Recursion/Fibonacci.ts } // --------------------------- Terminal Output: --------------------------- // ------------ Iterative ------------ // Fibonacci Iterative 0: undefined // Fibonacci Iterative 1: 0 // Fibonacci Iterative 2: 1 // Fibonacci Iterative 3: 1 // Fibonacci Iterative 4: 2 // Fibonacci Iterative 5: 3 // Fibonacci Iterative 6: 5 // Fibonacci Iterative 9: 21 // Fibonacci Iterative 42: 165580141 // Fibonacci Iterative 50: 7778742049 // Fibonacci Iterative 100: 218922995834555200000 // ------------ Recursive ------------ // Input -1 is invalid -_-' // Input 0 is invalid -_-' // Fibonacci Recursive 1: 0 // Fibonacci Recursive 2: 1 // Fibonacci Recursive 3: 1 // Fibonacci Recursive 4: 2 // Fibonacci Recursive 5: 3 // Fibonacci Recursive 7: 8 // Fibonacci Recursive 8: 13 // Fibonacci Recursive 42: 165580141 ================================================ FILE: Algorithms/Searching/Basics.ts ================================================ var beasts = ['Centaur', 'Godzilla', 'Mosura', 'Minotaur', 'Hydra', 'Nessie']; beasts.indexOf('Godzilla'); // Index: 1 beasts.findIndex(function(item){ // Index: 1 return item === 'Godzilla'; }); beasts.find(function(item){ // 'Godzilla' return item === 'Godzilla'; }); beasts.includes('Godzilla'); // true ================================================ FILE: Algorithms/Searching/BreadthFirstTraversal.ts ================================================ import BST from '../../Data-Structures/Trees/BinarySearchTree.ts'; import Node from '../../Data-Structures/Trees/BinaryTreeNode.ts'; import Queue from '../../Data-Structures/Sequential/Queue.ts'; function recursiveBFT(nodeQueue: Queue, nodesTraversed: Array): Array { if (nodeQueue.getLength() === 0) return nodesTraversed; let currentNode: Node = nodeQueue.dequeue(); nodesTraversed.push(currentNode.getValue()); if (currentNode.hasLeft()) nodeQueue.enqueue(currentNode.getLeft()); if (currentNode.hasRight()) nodeQueue.enqueue(currentNode.getRight()); return recursiveBFT(nodeQueue, nodesTraversed); } function breadthFirstTraversal(tree: BST) { const root = tree.getRoot(); if (!root) return false; const nodesTraversed: number[] = []; const nodeQueue = new Queue(); nodeQueue.enqueue(root); return recursiveBFT(nodeQueue, nodesTraversed); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const tree = new BST(); // 9 // 4 20 // 1 6 15 170 tree.insert(9); tree.insert(4); tree.insert(6); tree.insert(20); tree.insert(170); tree.insert(15); tree.insert(1); console.log(breadthFirstTraversal(tree)); // RUN: deno run Algorithms/Searching/BreadthFirstTraversal.ts } // --------------------------- Terminal Output: --------------------------- // [ // 9, 4, 20, 1, // 6, 15, 170 // ] ================================================ FILE: Algorithms/Searching/DepthFirstTraversals.ts ================================================ import BST from '../../Data-Structures/Trees/BinarySearchTree.ts'; import Node from '../../Data-Structures/Trees/BinaryTreeNode.ts'; export function inorderDFT(tree: BST) { const root = tree.getRoot(); if (!root) return false; const nodesTraversed: number[] = []; return traverseInOrder(root, nodesTraversed); /** * Recursive DFS on Binary Search Tree via inorder traversal * @param node Binary node to traverse * @param nodesTraversed Array of all nodes reached inorder */ function traverseInOrder(node: Node, nodesTraversed: Array) { if (node.hasLeft()) traverseInOrder(node.getLeft(), nodesTraversed); nodesTraversed.push(node.getValue()); if (node.hasRight()) traverseInOrder(node.getRight(), nodesTraversed); return nodesTraversed; } } export function preorderDFT(tree: BST) { const root = tree.getRoot(); if (!root) return false; const nodesTraversed: number[] = []; return traversePreOrder(root, nodesTraversed); /** * Recursive DFS on Binary Search Tree via preorder traversal * @param node Binary node to traverse * @param nodesTraversed Array of all nodes reached preorder */ function traversePreOrder(node: Node, nodesTraversed: Array) { nodesTraversed.push(node.getValue()); if (node.hasLeft()) traversePreOrder(node.getLeft(), nodesTraversed); if (node.hasRight()) traversePreOrder(node.getRight(), nodesTraversed); return nodesTraversed; } } export function postorderDFT(tree: BST) { const root = tree.getRoot(); if (!root) return false; const nodesTraversed: number[] = []; return traversePostOrder(root, nodesTraversed); /** * Recursive DFS on Binary Search Tree via postorder traversal * @param node Binary node to traverse * @param nodesTraversed Array of all nodes reached postorder */ function traversePostOrder(node: Node, nodesTraversed: Array) { if (node.hasLeft()) traversePostOrder(node.getLeft(), nodesTraversed); if (node.hasRight()) traversePostOrder(node.getRight(), nodesTraversed); nodesTraversed.push(node.getValue()); return nodesTraversed; } } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const tree = new BST(); // 9 // 4 20 // 1 6 15 170 tree.insert(9); tree.insert(4); tree.insert(6); tree.insert(20); tree.insert(170); tree.insert(15); tree.insert(1); console.log('Inorder:', inorderDFT(tree)); console.log('Preorder:', preorderDFT(tree)); console.log('Postorder:', postorderDFT(tree)); // RUN: deno run Algorithms/Searching/DepthFirstTraversals.ts } // --------------------------- Terminal Output: --------------------------- // Inorder: [ // 1, 4, 6, 9, // 15, 20, 170 // ] // Preorder: [ // 9, 4, 1, 6, // 20, 15, 170 // ] // Postorder: [ // 1, 6, 4, 15, // 170, 20, 9 // ] ================================================ FILE: Algorithms/Searching/Exercise_BFSDFSUseCases.md ================================================ # Graph Traversal Use Cases ## Which traversal algorithm would be best for each situation? (BFS or DFS) 1) If you know a solution is not far from the root of the tree: 2) If the tree is very deep and solutions are rare, 3) If the tree is very wide: 4) If solutions are frequent but located deep in the tree 5) determining whether a path exists between two nodes 6) Finding the shortest path ## Answers 1) BFS 2) BFS (DFS will take longer) 3) DFS (BFS is too memory intensive for wide trees) 4) DFS 5) DFS 6) BFS ================================================ FILE: Algorithms/Sorting/Basics.ts ================================================ const spanish = ['único', 'árbol', 'cosas', 'fútbol']; spanish.sort(function(a,b) { return a.localeCompare(b, 'es'); }); console.log(spanish); // OUTPUT: [ "cosas", "fútbol", "árbol", "único" ] // RUN: deno run Algorithms/Sorting/Basics.ts ================================================ FILE: Algorithms/Sorting/BubbleSort.ts ================================================ function bubbleSort(numbersArr: Array) { for (let i=0; i < numbersArr.length-1; ++i) { for (let j=0; j < numbersArr.length-1-i; ++j) { if (numbersArr[j] > numbersArr[j+1]) { numbersArr[j+1] = numbersArr[j] + numbersArr[j+1]; numbersArr[j] = numbersArr[j+1] - numbersArr[j]; numbersArr[j+1] = numbersArr[j+1] - numbersArr[j]; } } } return numbersArr; } function bubbleSortStrings(stringArr: Array) { let placeholder: string; for (let i=0; i < stringArr.length-1; ++i) { for (let j=0; j < stringArr.length-1-i; ++j) { if (stringArr[j] > stringArr[j+1]) { placeholder = stringArr[j]; stringArr[j] = stringArr[j+1]; stringArr[j+1] = placeholder; } } } return stringArr; } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const numbers1 = [9,6,5,3,1,8,7,2,4]; const numbers2 = [99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0]; const colors = ["white", "black", "green", "blue", "orange"]; console.log(bubbleSort(numbers1)); console.log(bubbleSort(numbers2)); console.log(bubbleSortStrings(colors)); // RUN: deno run Algorithms/Sorting/BubbleSort.ts } // --------------------------- Terminal Output: --------------------------- // [ // 1, 2, 3, 4, 5, // 6, 7, 8, 9 // ] // [ // 0, 1, 2, 4, 5, // 6, 44, 63, 87, 99, // 283 // ] // [ "black", "blue", "green", "orange", "white" ] ================================================ FILE: Algorithms/Sorting/Data/PlantFamilies.ts ================================================ export const plant_families = { short_list: [ "Loranthaceae", "Clusiaceae", "Aristolochiaceae", "Myrtaceae", "Araliaceae", "Ericaceae", "Dryopteridaceae", "Boraginaceae", "Juncaceae", "Pottiaceae", "Amaranthaceae", "Polemoniaceae", "Pteridaceae", "Arthopyreniaceae", "Campanulaceae", "Brachytheciaceae", "Caryophyllaceae", "Acanthaceae", "Sematophyllaceae", "Lamiaceae", "Lecanoraceae", "Brassicaceae", "Rosaceae", "Orchidaceae", "Sapotaceae", "Oleaceae", "Sapindaceae", "Amblystegiaceae", "Droseraceae", "Lecideaceae", "Gentianaceae", "Fabaceae", "Combretaceae", "Aspleniaceae", "Cyperaceae", "Arthoniaceae", "Chenopodiaceae", "Saxifragaceae", "Euphorbiaceae", "Passifloraceae", "Parmeliaceae", "Celastraceae", "Myristicaceae", "Andreaeaceae", "Poaceae", "Crassulaceae", "Asteraceae", "Stereocaulaceae", "Onagraceae", "Verrucariaceae", "Apiaceae", "Liliaceae", "Polygonaceae", "Convolvulaceae", "Scrophulariaceae", "Malvaceae", ], medium_list: [ "Burseraceae", "Buxbaumiaceae", "Encalyptaceae", "Hymeneliaceae", "Roccellaceae", "Peltigeraceae", "Ephedraceae", "Grammitidaceae", "Santalaceae", "Pyrolaceae", "Aristolochiaceae", "Placynthiaceae", "Marantaceae", "Ochnaceae", "Thelotremataceae", "Staphyleaceae", "Calycanthaceae", "Pleuroziopsidaceae", "Zingiberaceae", "Disceliaceae", "Proteaceae", "Selaginellaceae", "Sphagnaceae", "Theophrastaceae", "Malpighiaceae", "Limnanthaceae", "Viscaceae", "Gyalectaceae", "Cladoniaceae", "Graphidaceae", "Pyrenulaceae", "Geraniaceae", "Micareaceae", "Hippuridaceae", "Dryopteridaceae", "Dicranaceae", "Commelinaceae", "Krameriaceae", "Ruppiaceae", "Fumariaceae", "Thelenellaceae", "Splachnaceae", "Thymelaeaceae", "Najadaceae", "Simaroubaceae", "Araliaceae", "Vitaceae", "Corticiaceae", "Sarraceniaceae", "Zamiaceae", "Fontinalaceae", "Trypetheliaceae", "Lobariaceae", "Polytrichaceae", "Gentianaceae", "Acarosporaceae", "Equisetaceae", "Mniaceae", "Sapindaceae", "Cornaceae", "Ditrichaceae", "Catillariaceae", "Portulacaceae", "Cabombaceae", "Bryaceae", "Loasaceae", "Cistaceae", "Hypnaceae", "Dipsacaceae", "Crassulaceae", "Arecaceae", "Lecideaceae", "Pinaceae", "Plantaginaceae", "Juglandaceae", "Flacourtiaceae", "Combretaceae", "Melastomataceae", "Uncertain", "Ascomycota", "Family", "Monoblastiaceae", "Capparaceae", "Peltulaceae", "Pontederiaceae", "Araceae", "Rhamnaceae", "Amblystegiaceae", "Celastraceae", "Anacardiaceae", "Sapotaceae", "Piperaceae", "Hydrocharitaceae", "Sematophyllaceae", "Loranthaceae", "Primulaceae", "Potamogetonaceae", "Teloschistaceae", "Cuscutaceae", "Brachytheciaceae", "Opegraphaceae", "Bignoniaceae", "Melaspileaceae", "Dennstaedtiaceae", "Verrucariaceae", "Papaveraceae", "Isoetaceae", "Saururaceae", "Onagraceae", "Polygalaceae", "Pteridaceae", "Leskeaceae", "Pertusariaceae", "Nymphaeaceae", "Apocynaceae", "Nyctaginaceae", "Valerianaceae", "Rutaceae", "Rubiaceae", "Alectoriaceae", "Juncaceae", "Moraceae", "Orchidaceae", "Acanthaceae", "Solanaceae", "Urticaceae", "Myrtaceae", "Pottiaceae", "Parmeliaceae", "Aspleniaceae", "Cucurbitaceae", "Thelypteridaceae", "Linaceae", "Lecanoraceae", "Smilacaceae", "Stereocaulaceae", "Chrysobalanaceae", "Amaranthaceae", "Violaceae", "Styracaceae", "Cupressaceae", "Hydrangeaceae", "Hippocastanaceae", "Lauraceae", "Asclepiadaceae", "Tamaricaceae", "Aizoaceae", "Calymperaceae", "Porpidiaceae", "Andreaeaceae", "Cactaceae", "Bromeliaceae", "Mycoblastaceae", "Crossosomataceae", "Rhytidiaceae", "Orthotrichaceae", "Ericaceae", "Convolvulaceae", "Campanulaceae", "Magnoliaceae", "Euphorbiaceae", "Polemoniaceae", "Iridaceae", "Zygophyllaceae", "Orobanchaceae", "Saxifragaceae", "Caryophyllaceae", "Fagaceae", "Meteoriaceae", "Lamiaceae", "Neckeraceae", "Caprifoliaceae", "Malvaceae", "Lythraceae", "Aquifoliaceae", "Ranunculaceae", "Alismataceae", "Chenopodiaceae", "Physciaceae", "Hymenophyllaceae", "Theaceae", "Liliaceae", "Salicaceae", "Fabaceae", "Apiaceae", "Gesneriaceae", "Hydrophyllaceae", "Fissidentaceae", "Rosaceae", "Brassicaceae", "Verbenaceae", "Marcgraviaceae", "Boraginaceae", "Cyperaceae", "Agavaceae", "Lycopodiaceae", "Myrsinaceae", "Loganiaceae", "Scrophulariaceae", "Asteraceae", "Sterculiaceae", "Poaceae", "Polygonaceae", "Aceraceae", ], }; ================================================ FILE: Algorithms/Sorting/Exercise_SortingUseCases.md ================================================ # Sorting Use Cases ## Which sorting algorithm would be a good candidate for each situation? 1) Sort 10 schools around your house by distance. 2) eBay sorts listings by the current Bid amount. 3) Sport scores on ESPN. 4) Massive database (can't fit all into memory) needs to sort through past year's user data. 5) Almost sorted Udemy review data needs to update and add 2 new reviews. 6) Temperature Records for the past 50 years in Canada. 7) Large user name database needs to be sorted. Data is very random. 8) You want to teach sorting for the first time. ## Potential Candidates 1) Insertion Sort 2) Radix/Counting Sort 3) Quicksort 4) Merge Sort 5) Insertion Sort 6) Radix/Counting Sort or Quicksort 7) Mergesort/Quicksort 8) Bubble Sort, Selection Sort ================================================ FILE: Algorithms/Sorting/InsertionSort.ts ================================================ import { plant_families } from './Data/PlantFamilies.ts'; function insertionSort(inputArr: number[] | string[]) { if (typeof inputArr[0] === 'number') { for (let i=0; i < inputArr.length; ++i) { if (inputArr[i] < inputArr[0]) { const element = inputArr.splice(i,1) as number[]; // Move element to the first position (inputArr as number[]).unshift(element[0]); } else { // Only sort number smaller than preceding number if (inputArr[i] < inputArr[i-1]) { // Find where element's sorted position for (var j = 1; j < i; j++) { if (inputArr[i] >= inputArr[j-1] && inputArr[i] < inputArr[j]) { // Move element to the sorted spot inputArr.splice(j,0,inputArr.splice(i,1)[0] as number); } } } } } } else if (typeof inputArr[0] === 'string') { for (let i=0; i < inputArr.length; ++i) { if (inputArr[i] < inputArr[0]) { const element = inputArr.splice(i,1) as string[]; // Move element to the first position (inputArr as string[]).unshift(element[0]); } else { // Only sort number smaller than preceding number if (inputArr[i] < inputArr[i-1]) { // Find where element's sorted position for (var j = 1; j < i; j++) { if (inputArr[i] >= inputArr[j-1] && inputArr[i] < inputArr[j]) { // Move element to the sorted spot inputArr.splice(j,0,inputArr.splice(i,1)[0] as string); } } } } } } return inputArr; } function executionTime(method: any): string { const t0 = performance.now(); method(plant_families.medium_list); const t1 = performance.now(); return (t1-t0) + 'ms'; } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const numbers1 = [9,6,5,3,1,8,7,2,4]; const numbers2 = [99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0]; const colors = ["white", "black", "green", "blue", "orange"]; console.log('\n------------------ Insertion Sort ------------------'); console.log(insertionSort(numbers1)); console.log(insertionSort(numbers2)); console.log(insertionSort(colors)); console.log('\n---------------- Algorithm Benchmarks ----------------'); const a1 = Object.create({}); a1.run_1 = executionTime(insertionSort); a1.run_2 = executionTime(insertionSort); a1.run_3 = executionTime(insertionSort); // const a2 = Object.create({}); // a2.run_1 = executionTime(insertionSortModified); // a2.run_2 = executionTime(insertionSortModified); // a2.run_3 = executionTime(insertionSortModified); console.table([a1]); // RUN: deno run Algorithms/Sorting/InsertionSort.ts } // --------------------------- Terminal Output: --------------------------- // // ------------------ Insertion Sort ------------------ // [ // 1, 2, 3, 4, 5, // 6, 7, 8, 9 // ] // [ // 0, 1, 2, 4, 5, // 6, 44, 63, 87, 99, // 283 // ] // [ "black", "blue", "green", "orange", "white" ] // // ------------- Modified Insertion Sort -------------- // [ // 1, 2, 3, 4, 5, // 6, 7, 8, 9 // ] // [ // 0, 1, 2, 4, 5, // 6, 44, 63, 87, 99, // 283 // ] // [ "black", "blue", "green", "orange", "white" ] // // ---------------- Algorithm Benchmarks ---------------- // ┌───────┬────────┬────────┬───────┐ // │ (idx) │ run_1 │ run_2 │ run_3 │ // ├───────┼────────┼────────┼───────┤ // │ 0 │ "32ms" │ "34ms" │ "2ms" │ // │ 1 │ "10ms" │ "12ms" │ "4ms" │ // └───────┴────────┴────────┴───────┘ ================================================ FILE: Algorithms/Sorting/MergeSort.ts ================================================ function merge(left: any[], right: any[]): Array { const result = []; let leftIndex = 0; let rightIndex = 0; while ( leftIndex < left.length && rightIndex < right.length ) { if (left[leftIndex] <= right[rightIndex]) { result.push(left[leftIndex]); ++leftIndex; } else { result.push(right[rightIndex]); ++rightIndex; } } // console.log('Result:', result); // console.log('\tLeft:', left.slice(leftIndex)); // console.log('\tRight:', right.slice(rightIndex)); return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex)); // Add remaining elements on either side } function mergeSort(inputArr: string[] | number[]): Array { if (inputArr.length === 1) return inputArr; // Base Case // Split Array in into right and left const middleIndex = Math.floor(inputArr.length / 2); const left = inputArr.slice(0, middleIndex); const right = inputArr.slice(middleIndex); // console.log('left:', left); // console.log('right:', right); return merge( mergeSort(left), mergeSort(right), ); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const numbers1 = [9, 6, 5, 3, 1, 8, 7, 2, 4]; const numbers2 = [99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0]; const colors = ["white", "black", "green", "blue", "orange"]; console.log(mergeSort(numbers1)); console.log(mergeSort(numbers2)); console.log(mergeSort(colors)); // RUN: deno run Algorithms/Sorting/MergeSort.ts } // --------------------------- Terminal Output: --------------------------- // [ // 1, 2, 3, 4, 5, // 6, 7, 8, 9 // ] // [ // 0, 1, 2, 4, 5, // 6, 44, 63, 87, 99, // 283 // ] // [ "black", "blue", "green", "orange", "white" ] ================================================ FILE: Algorithms/Sorting/QuickSort.ts ================================================ /** * Given an array, swaps two elements at the specified positions (indices). * @param array Array in which to swap elements between two positions * @param pos1 Index of first element's position * @param pos2 Index of second element's position */ function swap(array: Array, pos1: number, pos2: number) { let temp = array[pos1]; array[pos1] = array[pos2]; array[pos2] = temp; } function partition(array: Array, pivotIndex: number, leftBound: number, rightBound: number){ let pivot = array[pivotIndex]; let partitionIndex = leftBound; for (let i = leftBound; i < rightBound; ++i) { if (array[i] < pivot) { swap(array, i, partitionIndex); ++partitionIndex; } } swap(array, rightBound, partitionIndex); return partitionIndex; } /** * Sort an array of numbers or strings using Quick Sort * @param array Complete array to be sorted * @param leftBound Index of left-most element for section to be sorted * @param rightBound Index of right-most element for section to be sorted * @returns Returns sorted array */ function quickSort(array: number[] | string[], leftBound: number, rightBound: number): Array { let pivotIndex: number; let partitionIndex: number; if (leftBound < rightBound) { pivotIndex = rightBound; partitionIndex = partition(array, pivotIndex, leftBound, rightBound); // Sort left & right sub-sections quickSort(array, leftBound, partitionIndex-1); quickSort(array, partitionIndex+1, rightBound); } return array; } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const numbers1 = [9, 6, 5, 3, 1, 8, 7, 2, 4]; const numbers2 = [99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0]; const colors = ["white", "black", "green", "blue", "orange"]; console.log(quickSort(numbers1, 0, numbers1.length-1)); console.log(quickSort(numbers2, 0, numbers2.length-1)); console.log(quickSort(colors, 0, colors.length-1)); // RUN: deno run Algorithms/Sorting/QuickSort.ts } // --------------------------- Terminal Output: --------------------------- // [ // 1, 2, 3, 4, 5, // 6, 7, 8, 9 // ] // [ // 0, 1, 2, 4, 5, // 6, 44, 63, 87, 99, // 283 // ] // [ "black", "blue", "green", "orange", "white" ] ================================================ FILE: Algorithms/Sorting/SelectionSort.ts ================================================ function swap(pos1: number, pos2: number, inputArr: Array): void { const placeholder = inputArr[pos1]; inputArr[pos1] = inputArr[pos2]; inputArr[pos2] = placeholder; } function selectionSort(inputArr: Array | Array): Array { let minValue: number | string; let minIndex: number; for (let i=0; i < inputArr.length-1; ++i) { minValue = inputArr[i]; minIndex = i; for (let j=i+1; j <= inputArr.length-1; ++j) { if (inputArr[j] < minValue) { minValue = inputArr[j]; minIndex = j; } } swap(i, minIndex, inputArr); } return inputArr; } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const numbers1 = [9,6,5,3,1,8,7,2,4]; const numbers2 = [99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0]; const colors = ["white", "black", "green", "blue", "orange"]; console.log(selectionSort(numbers1)); console.log(selectionSort(numbers2)); console.log(selectionSort(colors)); // RUN: deno run Algorithms/Sorting/SelectionSort.ts } // --------------------------- Terminal Output: --------------------------- // [ // 1, 2, 3, 4, 5, // 6, 7, 8, 9 // ] // [ // 0, 1, 2, 4, 5, // 6, 44, 63, 87, 99, // 283 // ] // [ "black", "blue", "green", "orange", "white" ] ================================================ FILE: Big-O/Ex1.ts ================================================ // Exercise 1 ----------------------------------------- function anotherFunction() { // unknown runtime } function funChallenge(input: Array): number { let a = 10; // O(1) a = 50 + 3; // O(1) for (let i = 0; i < input.length; i++) { // O(n) anotherFunction(); // O(n) let stranger = true; // O(n) a++; // O(n) } return a; // O(1) } const numbersArr = [1,2,3,4,5]; funChallenge(numbersArr); // Time Complexity: O(3 + 4n) ~ O(n) ================================================ FILE: Big-O/Ex2.ts ================================================ // Exercise 2 ----------------------------------------- function anotherFunChallenge(input: number): void { let a = 5; // O(1) let b = 10; // O(1) let c = 50; // O(1) for (let i = 0; i < input; i++) { // O(n) let x = i + 1; // O(n) let y = i + 2; // O(n) let z = i + 3; // O(n) } for (let j = 0; j < input; j++) { // O(n) let p = j * 2; // O(n) let q = j * 2; // O(n) } let whoAmI = "I don't know"; // O(1) } anotherFunChallenge(7); // Time Complexity: O(4 + 7n) ~ O(n) ================================================ FILE: Big-O/Hello_Big_O.ts ================================================ const nemo = ['nemo']; const fishColony = ['dory', 'bruce', 'marlin', 'nemo', 'gill', 'bloat', 'nigel', 'squirt', 'darla', 'hank']; const largeArr = new Array(10_000_000).fill('squish'); function findNemo(fishes: Array): string { let t0 = performance.now(); // O(1) let isNemoFound = false; // O(1) for (let i = 0; i < fishes.length; i++) { // O(n) if (fishes[i] === 'nemo') { // O(n) isNemoFound = true; // O(n) break; // O(1) } } let t1 = performance.now(); // O(1) console.log("Call to find Nemo took " + (t1 - t0) + "ms."); // O(1) return isNemoFound ? "Found NEMO!" : "Where did Nemo go?!"; // O(1) } function getFirstFish(fishes: Array) { return fishes[0].charAt(0).toUpperCase() + fishes[0].substring(1).toLowerCase(); // O(1) } console.log(findNemo(fishColony)); // Time Complexity: O(n) console.log(findNemo(largeArr)); // Time Complexity: O(n) console.log(getFirstFish(fishColony), "just keeps swimming!"); // Time Complexity: O(1) ================================================ FILE: Big-O/README.md ================================================ # Algorithm Time-Complexity Analysis **Goal:** Understand how the runtime of an algorithm is affected by an increasing number of elements. ## 5 Rules 1. Analyze the worst case performance of the algorithm, i.e. Big O 2. Add steps in order (+); multiply nested steps (*) 3. Different inputs should have different variables, e.g. O(a+b) 4. Remove constants 5. Drop non-dominants ## 3 Types ### 1. Big O – Worst Case #### Ideal O(1) – Constant O(log n) – Logarithmic O(n) – Linear #### Acceptable O(n * log n) – Log Linear #### Avoid O(n^2) – Quadratic O(2^n) – Exponential O(n!) – Factorial ### 2. Big Θ – Average/Tight Case ### 3. Big Ω – Best Case ## Resources - [Big-O Algorithm Complexity Cheat Sheet (Know Thy Complexities!) @ericdrowell](https://www.bigocheatsheet.com/ "Big O Cheat Sheet") - [Practical Java Examples of the Big O Notation](https://www.baeldung.com/java-algorithm-complexity "Big O Examples") ================================================ FILE: Big-O/Rule3.ts ================================================ // Rule 3: Different inputs should have different variables.-------------------- function compressBoxes(fullBoxes: Array, emptyBoxes: Array) { fullBoxes.forEach((box) => { // O(a) console.log(box); }); emptyBoxes.forEach((box) => { // O(b) console.log(box); }); } // Time Complexity: O(a+b) ================================================ FILE: Big-O/n_squared.ts ================================================ // Examples of Quadratic Big O Runtime — O(n^2) ------------------ const boxes = ["a", "b", "c", "d", "e"]; function logAllPairs(array: Array): void { for (let i = 0; i < array.length; i++) { // O(n) for (let j = 0; j < array.length; j++) { // O(n) console.log(array[i], array[j]); // O(n) } } } function printNumbersThenPairSums(numbers: Array): void { console.log("these are the numbers:"); numbers.forEach(function (number) { // O(n) console.log(number); }); console.log("and these are their sums:"); numbers.forEach(function (firstNumber) { // O(n) numbers.forEach(function (secondNumber) { // O(n) console.log(firstNumber + secondNumber); // O(n) }); }); } logAllPairs(boxes); // O(n * 2n) ~ O(n^2) printNumbersThenPairSums([1, 2, 3, 4, 5]); // O(n + n*2n) ~ O(n^2) ================================================ FILE: Data-Structures/Arrays/Basics.ts ================================================ // RUN: deno run Data-Structures/Arrays/Basics.ts const teamAvatar = ['Aang','Appa','Momo','Katara','Sokka']; // Push adds an element to the end of the array teamAvatar.push('Yue'); // O(1) // Pop removes and returns the last element console.log(teamAvatar.pop(), 'turned into the moon!!!'); // O(1) console.log(teamAvatar.pop(), "is in disbelief..."); // O(1) console.log(teamAvatar); console.log('Team Avatar is', teamAvatar.push('Sokka'), 'strong.'); // Unshift adds the specified elements to the beginning of the array teamAvatar.unshift('Toph'); // O(n) console.log('Toph joins the team!'); // Splice(i, n) removes n elements starting from index i teamAvatar.splice(2, 1); // O(n) console.log('Appa was captured?!?!', teamAvatar); // Splice(i, 0, [elements]) inserts [elements] at index i teamAvatar.splice(2, 0, 'Appa'); // O(n) console.log('Zuko saved Appa!!!', teamAvatar); ================================================ FILE: Data-Structures/Arrays/MyArray.ts ================================================ type NumIndexedObject = { [index: number]: any }; export default class MyArray { public length: number; private data: NumIndexedObject; constructor() { this.length = 0; this.data = Object.create({}); } /** * Get element at given index. * @param index Index of value to return * @returns Value, or null if non-existant */ public get(index: number): T | null { if(index > 0 && index < this.length) { return this.data[index]; } return null; } /** * Add element to end of array,i.e. at index Array.length. * @param item Value/object to push * @returns Length of array after push */ public push(item: T): number { this.data[this.length] = item; // Add item to end of array ++this.length; // Add 1 to array length return this.length; } /** * Remove the last element of array. * @returns Value/object at last index, or null if empty array */ public pop(): T | null { if(this.length > 0) { const lastItem = this.data[this.length-1]; // Retrieve last item delete this.data[this.length-1]; // Delete last item --this.length; // Decrement array length by 1 return lastItem; } return null; } /** * Delete item at given index. * @param index Numerical position to delete * @returns Value/object at index, or null if empty array */ public deleteIndex(index: number): T | null { if(index >= 0 && index < this.length) { const requestedItem = this.data[index]; this._shiftItemsLeftAfterIndex(index); return requestedItem; } return null; } /** * Insert a given value (item) at specified index * @param index Numerical position to insert at * @param item Value/object to insert * @returns Length of array after insertion, or null if failed insertion */ public insertItemAtIndex(index: number, item: T): number | null { if(index >= 0 && index < this.length) { this._shiftItemsRightAtIndex(index); this.data[index] = item; return this.length; } return null; } private _shiftItemsLeftAfterIndex(index: number): void { for (let i=index; i < this.length-1; ++i) { this.data[i] = this.data[i+1]; } --this.length; delete this.data[this.length]; } private _shiftItemsRightAtIndex(index: number): void { ++this.length; for (let i=this.length-1; i > index; --i) { this.data[i] = this.data[i-1]; } delete this.data[index]; } } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { let helloArray = new MyArray(); helloArray.push('Hello'); // O(1) helloArray.push('world'); console.log(helloArray); helloArray.pop(); // O(1) console.log(helloArray); helloArray.push('Deno'); helloArray.push('!'); console.log(helloArray); console.log('At index 2:', helloArray.get(2)); // ------------------------------------------- let sokka = new MyArray(); sokka.push('s'); sokka.push('o'); sokka.push('c'); sokka.push('k'); sokka.push('a'); console.log(sokka); console.log('Element deleted:', sokka.deleteIndex(2)); // O(n) console.log(sokka); sokka.insertItemAtIndex(2, 'k'); // O(n) console.log(sokka); // RUN: deno run Data-Structures/Arrays/Implementation.ts } // --------------------------- Terminal Output: --------------------------- // MyArray { length: 2, data: { 0: "Hello", 1: "world" } } // MyArray { length: 1, data: { 0: "Hello" } } // MyArray { length: 3, data: { 0: "Hello", 1: "Deno", 2: "!" } } // At index 2: ! // MyArray { length: 5, data: { 0: "s", 1: "o", 2: "c", 3: "k", 4: "a" } } // Element deleted: c // MyArray { length: 4, data: { 0: "s", 1: "o", 2: "k", 3: "a" } } // MyArray { length: 5, data: { 0: "s", 1: "o", 2: "k", 3: "k", 4: "a" } } ================================================ FILE: Data-Structures/Graphs/SimpleGraph.ts ================================================ type StrIndexedObject = { [index: string]: Set }; export default class SimpleGraph { private numberOfNodes: number; private adjacentList: StrIndexedObject; constructor() { this.numberOfNodes = 0; this.adjacentList = Object.create({}); } public getNodeCount(): number { return this.numberOfNodes; } public addVertex(node: string) { this.adjacentList[node] = new Set(); ++this.numberOfNodes; } public addEdge(node1: string, node2: string) { // Undirected, unweighted graph this.adjacentList[node1].add(node2); this.adjacentList[node2].add(node1); } public toString(): string { const allNodes = Object.keys(this.adjacentList); let representation: string = ""; for (let node of allNodes) { let nodeConnections = this.adjacentList[node]; let vertex; let count = 0; let connections = " { "; for (vertex of nodeConnections) { connections += vertex + (count < nodeConnections.size-1 ? ", ": " "); ++count; } representation += (node + "-->" + connections + '}\n'); } return representation; } } function printGraph(graph: SimpleGraph) { console.log(graph.toString()); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { let simpleGraph = new SimpleGraph(); simpleGraph.addVertex("0"); simpleGraph.addVertex("1"); simpleGraph.addVertex("2"); simpleGraph.addVertex("3"); simpleGraph.addVertex("4"); simpleGraph.addVertex("5"); simpleGraph.addVertex("6"); simpleGraph.addEdge("3", "1"); simpleGraph.addEdge("3", "4"); simpleGraph.addEdge("4", "2"); simpleGraph.addEdge("4", "5"); simpleGraph.addEdge("1", "2"); simpleGraph.addEdge("1", "0"); simpleGraph.addEdge("0", "2"); simpleGraph.addEdge("6", "5"); printGraph(simpleGraph); // RUN: deno run Data-Structures/Graphs/Graph.ts } // --------------------------- Terminal Output: --------------------------- // 0--> { 1, 2 } // 1--> { 3, 2, 0 } // 2--> { 4, 1, 0 } // 3--> { 1, 4 } // 4--> { 3, 2, 5 } // 5--> { 4, 6 } // 6--> { 5 } ================================================ FILE: Data-Structures/Hash-Tables/HashTable.ts ================================================ export class HashTable { private size: number; private data: Array>; constructor(numBuckets: number) { this.size = numBuckets; this.data = new Array(numBuckets); } public insert(key: string, value: any): void { let address = this._hash(key); if (!this.data[address]) { this.data[address] = []; } this.data[address].push([key,value]); } public get(key: string): any { let address = this._hash(key); const currentBucket: any[] = this.data[address]; if (currentBucket.length) { for(let item of currentBucket) { if (item[0] === key) return item[1]; } } return undefined; } public keys(): string[] { const keysArray = new Array(); // Loop through all buckets for (let i=0; i < this.data.length; ++i) { // If bucket has item(s) if (this.data[i]) { // Handle Collisions: Grab all keys from bucket // (incl. multiple items) for(let item of this.data[i]) { keysArray.push(item[0]); } } } return keysArray; } public values(): string[] { const valuesArray = new Array(); for (let i=0; i < this.data.length; ++i) { if (this.data[i]) { for(let item of this.data[i]) { valuesArray.push(item[1]); } } } return valuesArray; } public getSize(): number { return this.size; } private _hash(key: string) { let hash = 0; for (let i=0; i < key.length; ++i) { hash = (hash + key.charCodeAt(i) * i) % this.data.length; } return hash; } public testHashFunction() { console.log(this._hash('grapes')); console.log(this._hash('grapess')); console.log(this._hash('grapes')); } } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const hashTable = new HashTable(16); // hashTable.testHashFunction(); hashTable.insert('grapes', 27); // Θ(1) hashTable.insert('apples', 6); hashTable.insert('tangerines', 12); console.log('apples:', hashTable.get('apples')); console.log('grapes:', hashTable.get('grapes')); console.log(hashTable.keys()); console.log(hashTable.values()); // RUN: deno run Data-Structures/Hash-Tables/Implementation.ts } ================================================ FILE: Data-Structures/Linked-Lists/DoublyLinkedList.ts ================================================ import Node from './DoublyNode.ts'; export default class DoublyLinkedList { private head: Node | null; private tail: Node | null; private length: number; constructor() { this.head = null; this.tail = null; this.length = 0; } public getLength(): number { return this.length; } public isEmpty(): boolean { return this.length === 0; } public append(value: T, demo?: boolean): boolean { const newNode = new Node(value); if (!this.tail) { this.head = newNode; this.tail = newNode; } else { newNode.setPrevious(this.tail); this.tail.setNext(newNode); this.tail = this.tail.getNext(); } ++this.length; if (demo) { console.log('--------- Appending', value, 'at index', this.length-1); console.log(this.toString()); } return true; } public prepend(value: T, demo?: boolean): boolean { const newNode = new Node(value); if (!this.head) { this.head = newNode; this.tail = newNode; } else { newNode.setNext(this.head); this.head.setPrevious(newNode); this.head = newNode; } ++this.length; if (demo) { console.log('--------- Prepending', value, '---------'); console.log(this.toString()); } return true; } public insert(value: T, atIndex: number, demo?: boolean): boolean | null { if (atIndex < 0 || atIndex > this.length) return null; if (atIndex === 0) { this.prepend(value, demo); return true; } if (atIndex === this.length) { this.append(value, demo); return true; } const newNode = new Node(value); const leader = this._traverseToNode(atIndex-1); if (!leader) return false; const follower = leader.getNext(); newNode.setPrevious(leader); newNode.setNext(follower); leader.setNext(newNode); follower.setPrevious(newNode); ++this.length; if (demo) { console.log('--------- Inserting', value, 'at index', atIndex); console.log(this.toString()); } return true; } public getValueAtIndex(index: number): T | null { if (index > this.length-1 || index < 0) return null; // Validate input if (this.length === 0 || !this.head || !this.tail) return null; // Verify that list is not empty if (index === this.length-1) return this.tail.getValue(); // Optimization when retrieving last element let targetNode = this._traverseToNode(index); return targetNode?.getValue() || null; } /** * Find the index of the desired element if it exists. * WARNING: Usable only for trees of primitive types, i.e. number, string * @param value Value to search for, i.e. desired element * @returns Numerical index of element position */ public searchFor(value: T): number | null { if (this.length === 0 || !this.head) return null; let currentNode = this.head; for (let i=0; !!currentNode; ++i) { if (currentNode.getValue()===value) return i; // Value matches, so return index currentNode = currentNode.getNext(); // Otherwise, continue searching } return null; } /** * Remove all nodes from list. Constant Time O(1) */ public empty(nestedCall?: boolean): boolean { this.head = null; this.tail = null; this.length = nestedCall ? 1 : 0; return true; } public removeElementAtIndex(index: number, demo?: boolean): T | null { if (index > this.length-1 || index < 0) return null; if (this.length === 0 || !this.head) return null; let value = null; if (index===0) { value = this.head.getValue(); if (this.length > 1) { const newHead = this.head.getNext(); newHead.setPrevious(null); this.head.setNext(null); this.head = newHead; } else { this.empty(true); } } else { let leader = this._traverseToNode(index-1); const deletedNode = leader?.getNext(); value = deletedNode.getValue(); leader?.setNext(leader.getNext().getNext()); leader?.getNext().setPrevious(leader); deletedNode.setNext(null); deletedNode.setPrevious(null); } --this.length; if (demo) { console.log('Removing element at index', index + ':', JSON.stringify(value)); } return value; } private _traverseToNode(index: number): Node | null { if (!this.head || !this.tail) return null; let currentNode: Node; if (index < this.length/2) { currentNode = this.head; for (let i=0; iindex; --i) { currentNode = currentNode.getPrevious(); } } return currentNode; } public toString(nodesPerGroup?: number): string { if (this.length === 0 || !this.head) { return ""; } nodesPerGroup = nodesPerGroup ? nodesPerGroup : 6; const LABEL_HEAD = 'HEAD'; const LABEL_TAIL = 'TAIL'; let listAsString: string = `--- Node Count: ${this.length}\n`; let currentNode: Node = this.head; for (let i=0; i < this.length; ++i) { listAsString += `${Array(i%nodesPerGroup).fill('\t').join('')} ${i===0 ? LABEL_HEAD : i} ${currentNode} ${i===this.length-1 ? LABEL_TAIL : ''}\n`; if (currentNode.hasNext()) { currentNode = currentNode.getNext(); } } return listAsString; } } function printLinkedList(linkedList: DoublyLinkedList): void { if (linkedList.isEmpty()) { console.log('Empty linked list -_-'); return; } console.log(linkedList.toString()); } function printSearchFor(value: any, linkedList: DoublyLinkedList): void { console.log(JSON.stringify(value), 'found at index:', linkedList.searchFor(value)); } function printGetValueAtIndex(index: number, linkedList: DoublyLinkedList) { console.log('Element at index', index +':', linkedList.getValueAtIndex(index)); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const ATLA = new DoublyLinkedList(); ATLA.append('Sokka'); ATLA.append('Katara'); ATLA.prepend('Appa'); printLinkedList(ATLA); ATLA.insert('Aang', 2, true); ATLA.insert('Zuko', 1); ATLA.insert('Iroh', 5, true); ATLA.insert('Azula', 0, true); printSearchFor('Iroh', ATLA); printSearchFor('Sok', ATLA); printSearchFor('Sokka', ATLA); printSearchFor('Appa', ATLA); printSearchFor('Zuko', ATLA); printGetValueAtIndex(1, ATLA); printGetValueAtIndex(6, ATLA); console.log('----------------------------------'); ATLA.removeElementAtIndex(1, true); printLinkedList(ATLA); ATLA.removeElementAtIndex(0, true); printLinkedList(ATLA); ATLA.removeElementAtIndex(2, true); printLinkedList(ATLA); ATLA.removeElementAtIndex(2, true); printLinkedList(ATLA); ATLA.removeElementAtIndex(1, true); ATLA.removeElementAtIndex(0, true); ATLA.removeElementAtIndex(1, true); ATLA.removeElementAtIndex(0, true); printLinkedList(ATLA); console.log('----------------------------------'); console.log(); ATLA.insert('Katara', 0, true); ATLA.append('Aang'); printLinkedList(ATLA); // RUN: deno run Data-Structures/Linked-Lists/DoublyLinkedList.ts } // --------------------------- Terminal Output: --------------------------- // --- Node Count: 3 // HEAD { value: "Appa", next: true, previous: false } // 1 { value: "Sokka", next: true, previous: true } // 2 { value: "Katara", next: false, previous: true } TAIL // // --------- Inserting Aang at index 2 // --- Node Count: 4 // HEAD { value: "Appa", next: true, previous: false } // 1 { value: "Sokka", next: true, previous: true } // 2 { value: "Aang", next: true, previous: true } // 3 { value: "Katara", next: false, previous: true } TAIL // // --------- Appending Iroh at index 5 // --- Node Count: 6 // HEAD { value: "Appa", next: true, previous: false } // 1 { value: "Zuko", next: true, previous: true } // 2 { value: "Sokka", next: true, previous: true } // 3 { value: "Aang", next: true, previous: true } // 4 { value: "Katara", next: true, previous: true } // 5 { value: "Iroh", next: false, previous: true } TAIL // // --------- Prepending Azula --------- // --- Node Count: 7 // HEAD { value: "Azula", next: true, previous: false } // 1 { value: "Appa", next: true, previous: true } // 2 { value: "Zuko", next: true, previous: true } // 3 { value: "Sokka", next: true, previous: true } // 4 { value: "Aang", next: true, previous: true } // 5 { value: "Katara", next: true, previous: true } // 6 { value: "Iroh", next: false, previous: true } TAIL // // "Iroh" found at index: 6 // "Sok" found at index: null // "Sokka" found at index: 3 // "Appa" found at index: 1 // "Zuko" found at index: 2 // Element at index 1: Appa // Element at index 6: Iroh // ---------------------------------- // Removing element at index 1: "Appa" // --- Node Count: 6 // HEAD { value: "Azula", next: true, previous: false } // 1 { value: "Zuko", next: true, previous: true } // 2 { value: "Sokka", next: true, previous: true } // 3 { value: "Aang", next: true, previous: true } // 4 { value: "Katara", next: true, previous: true } // 5 { value: "Iroh", next: false, previous: true } TAIL // // Removing element at index 0: "Azula" // --- Node Count: 5 // HEAD { value: "Zuko", next: true, previous: false } // 1 { value: "Sokka", next: true, previous: true } // 2 { value: "Aang", next: true, previous: true } // 3 { value: "Katara", next: true, previous: true } // 4 { value: "Iroh", next: false, previous: true } TAIL // // Removing element at index 2: "Aang" // --- Node Count: 4 // HEAD { value: "Zuko", next: true, previous: false } // 1 { value: "Sokka", next: true, previous: true } // 2 { value: "Katara", next: true, previous: true } // 3 { value: "Iroh", next: false, previous: true } TAIL // // Removing element at index 2: "Katara" // --- Node Count: 3 // HEAD { value: "Zuko", next: true, previous: false } // 1 { value: "Sokka", next: true, previous: true } // 2 { value: "Iroh", next: false, previous: true } TAIL // // Removing element at index 1: "Sokka" // Removing element at index 0: "Zuko" // Removing element at index 0: "Iroh" // Empty linked list -_- // ---------------------------------- // // --------- Prepending Katara --------- // --- Node Count: 1 // HEAD { value: "Katara", next: false, previous: false } TAIL // // --- Node Count: 2 // HEAD { value: "Katara", next: true, previous: false } // 1 { value: "Aang", next: false, previous: true } TAIL ================================================ FILE: Data-Structures/Linked-Lists/DoublyNode.ts ================================================ export default class DoublyLLNode { private value: T; private next: DoublyLLNode | null; private previous: DoublyLLNode | null; constructor(value: T) { this.value = value; this.next = null; this.previous = null; } public setValue(value: T) { this.value = value; } public setNext(node: DoublyLLNode | null) { this.next = node; } public setPrevious(node: DoublyLLNode | null) { this.previous = node; } public getValue(): T { return this.value; } public getNext(): DoublyLLNode | any { return this.next; } public getPrevious(): DoublyLLNode | any { return this.previous; } public hasNext(): boolean { return !!this.next; } public hasPrevious(): boolean { return !!this.previous; } public toString(): string { return `{ value: ${JSON.stringify(this.value)}, next: ${!!this.next}, previous: ${!!this.previous} }`; } } ================================================ FILE: Data-Structures/Linked-Lists/LinkedList.ts ================================================ import Node from './SinglyNode.ts'; export default class LinkedList { private head: Node | null; private tail: Node | null; private length: number; constructor() { this.head = null; this.tail = null; this.length = 0; } public getLength(): number { return this.length; } public getHeadValue(): T | any { return this.head?.getValue(); } public getTailValue(): T | any { return this.tail?.getValue(); } public isEmpty(): boolean { return this.length === 0; } /** * Remove all nodes from list. Constant Time O(1) */ public empty(): boolean { this.head = null; this.tail = null; this.length = 0; return true; } public append(value: T): boolean { const newNode = new Node(value); if (!this.tail) { this.head = newNode; this.tail = newNode; } else { this.tail.setNext(newNode); this.tail = this.tail.getNext(); } ++this.length; return true; } public prepend(value: T): boolean { const newNode = new Node(value); if (!this.head) { this.head = newNode; this.tail = newNode; } else { newNode.setNext(this.head); this.head = newNode; } ++this.length; return true; } public insert(value: T, atIndex: number): boolean | null { if (atIndex < 0 || atIndex > this.length) return null; if (!this.head) { this.prepend(value); return true; } if (atIndex === this.length) { this.append(value); return true; } const newNode = new Node(value); let currentNode = this._traverseToNode(atIndex-1); newNode.setNext(currentNode?.getNext()); currentNode?.setNext(newNode); ++this.length; return true; } public removeElementAtIndex(index: number): T | null { if (index > this.length-1 || index < 0) return null; if (this.length === 0 || !this.head) return null; let value = null; if (index===0) { value = this.head.getValue(); this.head = this.head.getNext(); // Manipulate pointers to mark deleted Node for garbage collection } else { let leader = this._traverseToNode(index-1); value = leader?.getNext().getValue(); leader?.setNext(leader.getNext().getNext()); // Manipulate pointers to mark deleted Node for garbage collection } --this.length; // Decrement length return value; } public getValueAtIndex(index: number): T | null { if (index > this.length-1 || index < 0) return null; // Validate input if (this.length === 0 || !this.head || !this.tail) return null; // Verify that list is not empty if (index === this.length-1) return this.tail.getValue(); // Optimization when retrieving last element let targetNode = this._traverseToNode(index); return targetNode?.getValue() || null; } /** * Find the index of the desired element if it exists. * WARNING: Usable only for trees of primitive types, i.e. number, string * @param value Value to search for, i.e. desired element * @returns Numerical index of element position */ public searchFor(value: T): number | null { if (this.length === 0 || !this.head) return null; let currentNode = this.head; for (let i=0; !!currentNode; ++i) { if (currentNode.getValue()===value) return i; // Value matches, so return index currentNode = currentNode.getNext(); // Otherwise, continue searching } return null; } private _traverseToNode(index: number): Node | null { if (!this.head) return null; let currentNode = this.head; for (let i=0; i | any { if (this.length <= 0) return null; const array = new Array(this.length); let currentNode = this.head; for (let i=0; !!currentNode; ++i) { array[i] = currentNode.getValue(); currentNode = currentNode.getNext(); } return array; } public toString(nodesPerGroup?: number): string { if (this.length === 0 || !this.head) { return ""; } nodesPerGroup = nodesPerGroup ? nodesPerGroup : 6; const LABEL_HEAD = 'HEAD'; const LABEL_TAIL = 'TAIL'; let listAsString: string = `--- Node Count: ${this.length}\n`; let currentNode: Node = this.head; for (let i=0; i < this.length; ++i) { listAsString += `${Array(i%nodesPerGroup).fill('\t').join('')} ${i===0 ? LABEL_HEAD : i} ${currentNode} ${i===this.length-1 ? LABEL_TAIL : ''}\n`; if (currentNode.hasNext()) { currentNode = currentNode.getNext(); } } return listAsString; } } function printLinkedList(linkedList: LinkedList): void { if (linkedList.isEmpty()) { console.log('Empty linked list -_-'); return; } console.log(linkedList.toString()); } function printSearchFor(value: any, linkedList: LinkedList): void { console.log(JSON.stringify(value), 'found at index:', linkedList.searchFor(value)); } function printGetValueAtIndex(index: number, linkedList: LinkedList) { console.log('Element at index', index +':', linkedList.getValueAtIndex(index)); } function printRemoveIndex(index: number, linkedList: LinkedList) { console.log('Removing element at index', index + ':', JSON.stringify(linkedList.removeElementAtIndex(index))); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const ATLA = new LinkedList(); ATLA.append('Sokka'); ATLA.append('Katara'); ATLA.prepend('Appa'); printLinkedList(ATLA); ATLA.insert('Aang', 2); ATLA.insert('Zuko', 1); ATLA.insert('Iroh', 4); printLinkedList(ATLA); printSearchFor('Iroh', ATLA); printSearchFor('Sok', ATLA); printSearchFor('Sokka', ATLA); printSearchFor('Appa', ATLA); printSearchFor('Zuko', ATLA); printGetValueAtIndex(2, ATLA); printGetValueAtIndex(5, ATLA); console.log('------------ Reversing List ------------'); ATLA.reverse(); printLinkedList(ATLA); console.log('----------------------------------'); printRemoveIndex(1, ATLA); printLinkedList(ATLA); printRemoveIndex(0, ATLA); printLinkedList(ATLA); printRemoveIndex(2, ATLA); printLinkedList(ATLA); printRemoveIndex(2, ATLA); printLinkedList(ATLA); printRemoveIndex(1, ATLA); printRemoveIndex(0, ATLA); printLinkedList(ATLA); console.log('----------------------------------'); ATLA.insert('Katara', 0); printLinkedList(ATLA); ATLA.append('Aang'); printLinkedList(ATLA); console.log('------------ Reversing List ------------'); ATLA.reverse(); printLinkedList(ATLA); // RUN: deno run Data-Structures/Linked-Lists/LinkedList.ts } // --------------------------- Terminal Output: --------------------------- // --- Node Count: 3 // HEAD { value: "Appa", next: true } // 1 { value: "Sokka", next: true } // 2 { value: "Katara", next: false } TAIL // // --- Node Count: 6 // HEAD { value: "Appa", next: true } // 1 { value: "Zuko", next: true } // 2 { value: "Sokka", next: true } // 3 { value: "Aang", next: true } // 4 { value: "Iroh", next: true } // 5 { value: "Katara", next: false } TAIL // // "Iroh" found at index: 4 // "Sok" found at index: null // "Sokka" found at index: 2 // "Appa" found at index: 0 // "Zuko" found at index: 1 // Element at index 2: Sokka // Element at index 5: Katara // ------------ Reversing List ------------ // --- Node Count: 6 // HEAD { value: "Katara", next: true } // 1 { value: "Iroh", next: true } // 2 { value: "Aang", next: true } // 3 { value: "Sokka", next: true } // 4 { value: "Zuko", next: true } // 5 { value: "Appa", next: false } TAIL // // ---------------------------------- // Removing element at index 1: "Iroh" // --- Node Count: 5 // HEAD { value: "Katara", next: true } // 1 { value: "Aang", next: true } // 2 { value: "Sokka", next: true } // 3 { value: "Zuko", next: true } // 4 { value: "Appa", next: false } TAIL // // Removing element at index 0: "Katara" // --- Node Count: 4 // HEAD { value: "Aang", next: true } // 1 { value: "Sokka", next: true } // 2 { value: "Zuko", next: true } // 3 { value: "Appa", next: false } TAIL // // Removing element at index 2: "Zuko" // --- Node Count: 3 // HEAD { value: "Aang", next: true } // 1 { value: "Sokka", next: true } // 2 { value: "Appa", next: false } TAIL // // Removing element at index 2: "Appa" // --- Node Count: 2 // HEAD { value: "Aang", next: true } // 1 { value: "Sokka", next: false } TAIL // // Removing element at index 1: "Sokka" // Removing element at index 0: "Aang" // Empty linked list -_- // ---------------------------------- // --- Node Count: 1 // HEAD { value: "Katara", next: false } TAIL // // --- Node Count: 2 // HEAD { value: "Katara", next: true } // 1 { value: "Aang", next: false } TAIL // // ------------ Reversing List ------------ // --- Node Count: 2 // HEAD { value: "Aang", next: true } // 1 { value: "Katara", next: false } TAIL ================================================ FILE: Data-Structures/Linked-Lists/SinglyNode.ts ================================================ export default class LinkedListNode { private value: T; private next: LinkedListNode | null; constructor(value: T) { this.value = value; this.next = null; } public setValue(value: T) { this.value = value; } public setNext(nextNode: LinkedListNode | null) { this.next = nextNode; } public getValue(): T { return this.value; } public getNext(): LinkedListNode | any { return this.next; } public hasNext(): boolean { return !!this.next; } public toString(): string { return `{ value: ${JSON.stringify(this.value)}, next: ${!!this.next} }`; } } ================================================ FILE: Data-Structures/README.md ================================================ # Data Structures ## Core - [X] Arrays - [X] Hash Tables - [X] Stacks - [X] Queues - [X] Linked Lists - [X] Trees - [X] Binary Search Tree (BST) - AVL Tree - Red Black Tree - Binary Heap - Priority Queue - Trie - [X] Graphs ## Resources - [The Data Structures Handbook](https://www.thedshandbook.com/ "DS Handbook") - [Comprehensive List of Data Structures](https://en.wikipedia.org/wiki/List_of_data_structures "Wikipedia: DS List") - [Visualizing Data Structures & Algorithms](https://visualgo.net/en) ### Foundations - [Arrays vs Linked Lists: Youtube](https://youtu.be/DyG9S9nAlUM) - [Linked List: VisuAlgo](https://visualgo.net/en/list) ### Graphs - [The Internet Map](https://internet-map.net/) - [Graphs: VisuAlgo](https://visualgo.net/en/graphds) ### Trees - [Binary Search Tree: VisuAlgo](https://visualgo.net/bn/bst?slide=1) - [Binary Heap: VisuAlgo](https://visualgo.net/en/heap) - [AVL Tree Visualization](https://www.cs.usfca.edu/~galles/visualization/AVLtree.html) - [Red-Black Tree Visualization](https://www.cs.usfca.edu/~galles/visualization/RedBlack.html) - [Priority Queue Implementation: GeeksForGeeks](https://www.cs.usfca.edu/~galles/visualization/RedBlack.html) - [Validate a BST: GeeksForGeeks](https://www.geeksforgeeks.org/a-program-to-check-if-a-binary-tree-is-bst-or-not/) ### Hash Tables - [MD5 Hash Generator](http://www.miraclesalad.com/webtools/md5.php) - [Hash Table Animation](https://www.cs.usfca.edu/~galles/visualization/OpenHash.html) - [Hash Table | Wikipedia](https://en.wikipedia.org/wiki/Hash_table) - [Associative Arrays | Wikipedia](https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(associative_array) "Hash Tables (aka Associative Arrays)") ================================================ FILE: Data-Structures/Sequential/Queue.ts ================================================ import Node from '../Linked-Lists/SinglyNode.ts'; export default class Queue { private last: Node | null; private first: Node| null; private length: number; constructor() { this.last = null; this.first = null; this.length = 0; } public getLength(): number { return this.length; } public isEmpty(): boolean { return this.length === 0; } public peek(): T | null { return this.first?.getValue() || null; } public enqueue(value: T): boolean { const newNode = new Node(value); if (!this.last) { // If empty queue, initialize this.first = newNode; this.last = newNode; } else { this.last.setNext(newNode); this.last = this.last.getNext(); } ++this.length; return true; } public dequeue(): T | any { if (!this.first) return null; // Edge case: Empty queue if (this.length === 1) { // Edge case: Queue has 1 element, so a dequeue should reset the queue's state this.last = null; // Challenge: What is the state of each 'class field' after a dequeue when 1 element was remaining? } const value = this.first.getValue(); this.first = this.first.getNext(); --this.length; return value; } } function printQueue(queue: Queue) { console.log(JSON.stringify(queue)); } function printPeekQueue(queue: Queue) { console.log('Peeking... Found', JSON.stringify(queue.peek())); } function printDequeue(queue: Queue) { console.log('Dequeued:', JSON.stringify(queue.dequeue())); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const ATLA = new Queue(); printPeekQueue(ATLA); ATLA.enqueue('Sokka'); ATLA.enqueue('Katara'); printPeekQueue(ATLA); ATLA.enqueue('Aang'); ATLA.enqueue('Appa'); printQueue(ATLA); printDequeue(ATLA); printDequeue(ATLA); printDequeue(ATLA); printQueue(ATLA); printDequeue(ATLA); printQueue(ATLA); ATLA.enqueue('Zuko'); ATLA.enqueue('Iroh'); printQueue(ATLA); // RUN: deno run Data-Structures/Sequential/Queue.ts } // --------------------------- Terminal Output: --------------------------- // Peeking... Found null // Peeking... Found "Sokka" // {"last":{"value":"Appa","next":null},"first":{"value":"Sokka","next":{"value":"Katara","next":{"value":"Aang","next":{"value":"Appa","next":null}}}},"length":4} // Dequeued: "Sokka" // Dequeued: "Katara" // Dequeued: "Aang" // {"last":{"value":"Appa","next":null},"first":{"value":"Appa","next":null},"length":1} // Dequeued: "Appa" // {"last":null,"first":null,"length":0} // {"last":{"value":"Iroh","next":null},"first":{"value":"Zuko","next":{"value":"Iroh","next":null}},"length":2} ================================================ FILE: Data-Structures/Sequential/Stack.ts ================================================ export default class Stack { private length: number; private values: Array; constructor() { this.length = 0; this.values = new Array(); } public getLength(): number { return this.length; } public isEmpty(): boolean { return this.length === 0; } public peek(): T | null { return this.values[this.length-1] || null; } public push(value: T): boolean { this.values[this.length] = value; ++this.length; return true; } public pop() { const value = this.values[this.length-1]; delete this.values[this.length-1]; --this.length; return value; } } function printStack(stack: Stack) { console.log(JSON.stringify(stack)); } function printPopStack(stack: Stack) { console.log('Popped:', stack.pop()); } function printPeekStack(stack: Stack) { console.log('Peeking... Found', stack.peek()); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const ATLA = new Stack(); printPeekStack(ATLA); ATLA.push('Sokka'); ATLA.push('Katara'); printPeekStack(ATLA); ATLA.push('Aang'); ATLA.push('Appa'); printStack(ATLA); printPopStack(ATLA); printStack(ATLA); printPopStack(ATLA); printPopStack(ATLA); printPopStack(ATLA); printStack(ATLA); ATLA.push('Zuko'); ATLA.push('Iroh'); printStack(ATLA); // RUN: deno run Data-Structures/Sequential/Stack.ts } // --------------------------- Terminal Output: --------------------------- // Peeking... Found null // Peeking... Found Katara // {"length":4,"values":["Sokka","Katara","Aang","Appa"]} // Popped: Appa // {"length":3,"values":["Sokka","Katara","Aang",null]} // Popped: Aang // Popped: Katara // Popped: Sokka // {"length":0,"values":[null,null,null,null]} // {"length":2,"values":["Zuko","Iroh",null,null]} ================================================ FILE: Data-Structures/Sequential/StackLL.ts ================================================ import Node from '../Linked-Lists/SinglyNode.ts'; export default class StackLL { private top: Node | null; private bottom: Node | null; private length: number; constructor() { this.top = null; this.bottom = null; this.length = 0; } public getLength(): number { return this.length; } public isEmpty(): boolean { return this.length === 0; } public peek(): T | null { return this.top?.getValue() || null; } public push(value: T): boolean { const newNode = new Node(value); if (!this.top) { // If stack is empty, initialize this.top = newNode; this.bottom = newNode; } else { // Else manipulate pointers newNode.setNext(this.top); this.top = newNode; } /* Challenge (Alternate Else Clause): * 1. How does the code below behave and why is it incorrect? * 2. What are the consequences to time complexity? */ // else { // this.top.setNext(newNode); // Else, set next node of LL to new value // this.top = this.top.getNext(); // And move 'top' pointer forward // } ++this.length; // Increment length return true; } public pop(): T | null { if (!this.top) return null; // If empty stack, return null if (this.length === 1) { this.bottom = null; } const value = this.top.getValue(); // Retrieve top value of stack this.top = this.top.getNext(); // Move top pointer to next node // On line 55, the reference to the top most node of the stack is lost // The node is now floating in memory, waiting patiently for the garbage collector --this.length; return value; } } function printStack(stack: StackLL) { console.log(JSON.stringify(stack)); } function printPopStack(stack: StackLL) { console.log('Popped:', stack.pop()); } function printPeekStack(stack: StackLL) { console.log('Peeking:', stack.peek()); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const ATLA = new StackLL(); printPeekStack(ATLA); ATLA.push('Sokka'); ATLA.push('Katara'); printPeekStack(ATLA); ATLA.push('Aang'); ATLA.push('Appa'); printStack(ATLA); printPopStack(ATLA); printStack(ATLA); printPopStack(ATLA); printPopStack(ATLA); printPopStack(ATLA); printStack(ATLA); ATLA.push('Zuko'); ATLA.push('Iroh'); printStack(ATLA); // RUN: deno run Data-Structures/Sequential/StackLL.ts } // --------------------------- Terminal Output: --------------------------- // Peeking: null // Peeking: Katara // {"top":{"value":"Appa","next":{"value":"Aang","next":{"value":"Katara","next":{"value":"Sokka","next":null}}}},"bottom":{"value":"Sokka","next":null},"length":4} // Popped: Appa // {"top":{"value":"Aang","next":{"value":"Katara","next":{"value":"Sokka","next":null}}},"bottom":{"value":"Sokka","next":null},"length":3} // Popped: Aang // Popped: Katara // Popped: Sokka // {"top":null,"bottom":null,"length":0} // {"top":{"value":"Iroh","next":{"value":"Zuko","next":null}},"bottom":{"value":"Zuko","next":null},"length":2} ================================================ FILE: Data-Structures/Trees/BinarySearchTree.test.ts ================================================ import { assertEquals, assertNotEquals } from '../../test_deps.ts'; import BinarySearchTree from './BinarySearchTree.ts'; //--------------------------------------------------------------------- // ---------- UNIT TESTS ---------- //--------------------------------------------------------------------- // RUN: deno test Data-Structures/Trees/BinarySearchTree.test.ts Deno.test({ name: "Compare Trees 1", fn() { const tree1 = new BinarySearchTree(); tree1.insert(30); tree1.insert(6); tree1.insert(92) const tree2 = new BinarySearchTree(); tree2.insert(30); tree2.insert(92); tree2.insert(6); assertEquals(tree1.equalsQuantum(tree2), true); } }) Deno.test({ name: "Compare Trees 2", fn() { const tree1 = new BinarySearchTree(); tree1.insert(30); tree1.insert(6); tree1.insert(92) const tree2 = new BinarySearchTree(); tree2.insert(30); tree2.insert(6); tree2.insert(91); assertEquals(tree1.equalsQuantum(tree2), false); } }) ================================================ FILE: Data-Structures/Trees/BinarySearchTree.ts ================================================ import BNode from './BinaryTreeNode.ts'; import Queue from '../Sequential/Queue.ts'; export default class BinarySearchTree { public static traverse(node: BNode | null) { if(node === null) return; const tree = Object.create({}); tree.value = node.getValue(); tree.left = node.getLeft() === null ? null : this.traverse(node.getLeft()); tree.right = node.getRight() === null ? null : this.traverse(node.getRight()); return tree; } // ------------------- Instance Code Starts Here ------------------- private root: BNode | null; constructor() { this.root = null; } public getRoot(): BNode | null { return this.root; } public insert(value: number) { // If empty tree, create root if(this.root === null) { this.root = new BNode(value); return this.root; } // Otherwise, traverse tree to find correct insert location let currentNode: BNode = this.root; while(true) { // Value is smaller than current node if (value < currentNode.getValue()) { if(!currentNode.getLeft()) { // If no left node on currentNode currentNode.setLeft(new BNode(value)); // Create left node with value return this; } else { currentNode = currentNode.getLeft(); // Otherwise, go into existing left node continue; // Small optimization } } // Value is greater than or equal to current node if (value >= currentNode.getValue()) { if(!currentNode.getRight()) { // If no right node on currentNode currentNode.setRight(new BNode(value)); // Create right node with value return this; } else { currentNode = currentNode.getRight(); // Otherwise, go into the existing right node continue; // Small optimization } } } } public lookup(value: number): BNode | null { let currentNode: BNode; if(!!this.root) { currentNode = this.root; } else { return null; } while(true) { // We found our value! if (value === currentNode.getValue()) { return currentNode; } if (value < currentNode.getValue()) { // Value is smaller than current node if(!!currentNode.getLeft()) { // If left child exists currentNode = currentNode.getLeft(); // Move into left child continue; } return null; // Otherwise No left child, value DNE } else { // Value is greater than current node if(!!currentNode.getRight()) { // If right child exists currentNode = currentNode.getRight(); // Move into right child continue; } return null; // Otherwise no right child, value DNE } } } public remove(value: number): boolean { let parentNode: BNode | null; let currentNode: BNode; if(!!this.root) { currentNode = this.root; parentNode = null; } else { return false; } while(true) { if (value < currentNode.getValue()) { parentNode = currentNode; currentNode = currentNode.getLeft() continue; } else if(value > currentNode.getValue()) { parentNode = currentNode; currentNode = currentNode.getRight(); continue; } else if(value === currentNode.getValue()){ // Found node to delete! // Decision Tree for Deleting a Node // Branch 1: No right child if (!currentNode.getRight()) { if (!parentNode) { this.root = currentNode.getLeft(); } else { if (currentNode.getValue() < parentNode.getValue()) { parentNode.setLeft(currentNode.getLeft()); } else if (currentNode.getValue() > parentNode.getValue()) { parentNode.setRight(currentNode.getLeft()); } } } // Branch 2: Right child w/ no left child else if (!currentNode.getRight().getLeft()) { if (!parentNode) { this.root = currentNode.getLeft(); } else { currentNode.getRight().setLeft(currentNode.getLeft()); if (currentNode.getValue() < parentNode.getValue()) { parentNode.setLeft(currentNode.getRight()); } else if (currentNode.getValue() > parentNode.getValue()) { parentNode.setRight(currentNode.getRight()); } } } // Branch 3: Right child w/ left child else { // Find the right child's left most child let leftmost = currentNode.getRight().getLeft(); let parentOfLeftmost = currentNode.getRight(); while(!!leftmost.getLeft()) { parentOfLeftmost = leftmost; leftmost = leftmost.getLeft(); } // Parent's left subtree is now leftmost's right subtree parentOfLeftmost.setLeft(leftmost.getRight()); leftmost.setLeft(currentNode.getLeft()); leftmost.setRight(currentNode.getRight()); if (!parentNode) { this.root = leftmost; } else { if (currentNode.getValue() < parentNode.getValue()) { parentNode.setLeft(leftmost); } else if (currentNode.getValue() > parentNode.getValue()) { parentNode.setRight(leftmost); } } } } return true; // Node removal was a success! } } public invertTree(): boolean { if (!this.root) false; this._invertNode(this.root); return true; } private _invertNode(current: BNode | null) { if (current === null) return; this._swapChildren(current); this._invertNode(current.getLeft()); this._invertNode(current.getRight()); } private _swapChildren(node: BNode) { const left = node.getLeft(); node.setLeft(node.getRight()); node.setRight(left); } public breadthFirstTraversal() { if (!this.root) return false; let currentNode = this.root; let nodesTraversed = new Array(); const nodeQueue: Queue = new Queue(); nodeQueue.enqueue(currentNode); while (nodeQueue.getLength() > 0) { currentNode = nodeQueue.dequeue(); nodesTraversed.push(currentNode.getValue()); if (currentNode.hasLeft()) nodeQueue.enqueue(currentNode.getLeft()); if (currentNode.hasRight()) nodeQueue.enqueue(currentNode.getRight()); } return nodesTraversed; } public equalsQuantum(tree: BinarySearchTree): boolean { return JSON.stringify(this) === JSON.stringify(tree); } } function printNode(tree: BinarySearchTree, value: number): void { const requestedNode = tree.lookup(value); console.log('Find', value + ':', !!requestedNode ? JSON.stringify(requestedNode) : 'Node not found.'); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const tree = new BinarySearchTree(); // 9 // 4 20 // 1 6 15 170 tree.insert(9); tree.insert(4); tree.insert(6); tree.insert(20); tree.insert(170); tree.insert(15); tree.insert(1); console.log(tree.breadthFirstTraversal()); // console.log('Tree: ', JSON.stringify(BinarySearchTree.traverse(tree.getRoot()))); tree.remove(20); printNode(tree, 4); printNode(tree, 17); printNode(tree, 40); printNode(tree, 170); console.log('Original Tree: ', JSON.stringify(BinarySearchTree.traverse(tree.getRoot()))); tree.invertTree(); console.log('Inverse Tree: ', JSON.stringify(BinarySearchTree.traverse(tree.getRoot()))); console.log(tree.breadthFirstTraversal()); // RUN: deno run Data-Structures/Trees/BinarySearchTree.ts } // --------------------------- Terminal Output: --------------------------- // [ // 9, 4, 20, 1, // 6, 15, 170 // ] // Tree: {"value":9,"left":{"value":4,"left":{"value":1,"left":null,"right":null},"right":{"value":6,"left":null,"right":null}},"right":{"value":20,"left":{"value":15,"left":null,"right":null},"right":{"value":170,"left":null,"right":null}}} // Find 4: {"value":4,"left":{"value":1,"left":null,"right":null},"right":{"value":6,"left":null,"right":null}} // Find 17: Node not found. // Find 40: Node not found. // Find 170: {"value":170,"left":{"value":15,"left":null,"right":null},"right":null} // Original Tree: {"value":9,"left":{"value":4,"left":{"value":1,"left":null,"right":null},"right":{"value":6,"left":null,"right":null}},"right":{"value":170,"left":{"value":15,"left":null,"right":null},"right":null}} // Inverse Tree: {"value":9,"left":{"value":170,"left":null,"right":{"value":15,"left":null,"right":null}},"right":{"value":4,"left":{"value":6,"left":null,"right":null},"right":{"value":1,"left":null,"right":null}}} // [ 9, 170, 4, 15, 6, 1 ] ================================================ FILE: Data-Structures/Trees/BinaryTreeNode.ts ================================================ export default class BinaryTreeNode { private value: number; private left: BinaryTreeNode | null; private right: BinaryTreeNode | null; constructor(value: number) { this.value = value; this.left = null; this.right = null; } public setValue(value: number) { this.value = value; } public setLeft(binaryTreeNode: BinaryTreeNode) { this.left = binaryTreeNode; } public setRight(binaryTreeNode: BinaryTreeNode) { this.right = binaryTreeNode; } public getValue(): number { return this.value; } public getLeft(): BinaryTreeNode | any { return this.left; } public getRight(): BinaryTreeNode | any { return this.right; } public hasLeft(): boolean { if (!!this.left) return true; return false; } public hasRight(): boolean { if (!!this.right) return true; return false; } } ================================================ FILE: Playground/Demos/Classes_101.ts ================================================ // RUN: deno run Playground/Demos/Classes_101.ts class Bender { protected name: string; protected isFemale: boolean; protected element: string; constructor(name: string, isFemale: boolean, element: string) { this.name = name; this.isFemale = isFemale; this.element = element; // console.log('bender', this); } public introduce() { console.log(`"Hi, I am ${this.name}. I'm from the ${this.element} nation!"`); } } class EarthBender extends Bender { protected specialty?: string; constructor(name: string, isFemale: boolean, specialty?: string) { super(name, isFemale, 'Earth'); this.specialty = specialty; // console.log('EarthBender', this); } public earthbend() { console.log(`${this.name} lifts the ground beneath ${this.isFemale ? 'her' : 'him'}!`); } } const toph = new EarthBender('Toph', true, 'metal'); const bumi = new EarthBender('Bumi', false); toph.introduce(); toph.earthbend(); bumi.introduce(); bumi.earthbend(); ================================================ FILE: Playground/Demos/Objects_101.ts ================================================ // RUN: deno run Playground/Demos/Objects_101.ts console.log('------------------------- Episode 1 -------------------------'); const episode1 = { name: 'The Boy in the Iceberg', location: ['Shipwreck', 'Southern Water Tribe', "Zuko's Ship"], characters: ['Aang', 'Appa', 'Katara', 'Sokka', 'Iroh', 'Zuko'], introduction: function() { return "Water. Earth. Fire. Air. Long ago, the four nations lived together in harmony."; } }; console.log(episode1); console.log('------------------------- Episode 2 -------------------------'); const episode2 = Object.create(episode1); console.log(episode2); // While fields don't show on console, console.log(episode2.name, '—', episode2['characters']); // they are still populated by passed in prototype. console.log(episode2.introduction()); // Function also exists because episode1 used as prototype. episode2.name = 'The Avatar Returns'; episode2['location'] = ['Southern Water Tribe', "Zuko's Ship"]; // Recommended assignment syntax episode2['characters'] = ['Aang', 'Appa', 'Katara', 'Sokka', 'Iroh', 'Zuko']; console.log(episode2); console.log('------------------------- Episode 3 -------------------------'); const episode3 = Object.create({}); // console.log(episode3.introduction()); // Does not exist because didn't use episode1 prototype episode3.name = 'The Southern Air Temple'; episode3['location'] = ['Earth Kingdom Harbor', 'Southern Air Temple']; // Recommended syntax episode3[1] = 'The Air Temple is one of the most beautiful places in the world!'; // Can also use syntax for numerical index in object console.log(episode3); ================================================ FILE: Playground/Interviews/HealthcareHM.ts ================================================ function findNumOccurrences(color: string, colorsArr: string[]): number { let occurrences = 0; for (let c of colorsArr) { if (color === c) ++occurrences; } return occurrences; } console.log(findNumOccurrences("black", ["white", "red", "black", "green", "black", "red"])); function unique(array: Array | Array): boolean { const found = new Set(); for (let element of array) { if (found.has(element)) return false; else found.add(element); } return true; } console.log(unique([1,4,6,8,2])); console.log(unique([1,4,6,8,4,2])); // RUN: deno run Playground/Interviews/HealthcareHM.ts ================================================ FILE: Playground/Puzzles/AngryFrogs.test.ts ================================================ import { assertEquals } from '../../test_deps.ts'; import maxDistance from './AngryFrogs.ts'; // RUN: deno test Playground/Puzzles/AngryFrogs.test.ts //--------------------------------------------------------------------- // ---------- UNIT TESTS ---------- //--------------------------------------------------------------------- Deno.test({ name: "[1,2,3,4,5] -> 4", fn() { let lilypadHeights = [1,2,3,4,5]; assertEquals(maxDistance(lilypadHeights), 4); } }); Deno.test({ name: "[5,7,3,1,2,3,4,6,2] -> 6", fn() { let lilypadHeights = [5,7,3,1,2,3,4,6,2]; assertEquals(maxDistance(lilypadHeights), 6); } }); ================================================ FILE: Playground/Puzzles/AngryFrogs.ts ================================================ export default function maxDistance(lilypadHeights: number[]) { Solution.initializeLilypadForest(lilypadHeights); return Solution.findMaxDistance(); } class Solution { private static lilypadForest: number[]; static initializeLilypadForest(lilypadHeights: number[]) { Solution.lilypadForest = lilypadHeights; } static findMaxDistance(): number { let maxDistance = 0; let distanceTraveledLeft, distanceTraveledRight, distanceTraveled; for(let i=0; i < Solution.lilypadForest.length; ++i) { // console.log("Lilypad", i); distanceTraveledRight = Solution._travelRightFrom(i); distanceTraveledLeft = Solution._travelLeftFrom(i); distanceTraveled = distanceTraveledLeft + distanceTraveledRight; // console.log(" Distance Traveled:", distanceTraveled) if(distanceTraveled > maxDistance) maxDistance = distanceTraveled; // console.log(" Max Distance:", maxDistance) } return maxDistance; } private static _travelRightFrom(index: number): number { for(let i=index; ; ++i) { if(i+1 == Solution.lilypadForest.length || Solution.lilypadForest[i+1] < Solution.lilypadForest[i]) { // console.log(" Traveled Right: ", i-index); return i-index; } } } private static _travelLeftFrom(index: number): number { for(let i=index; ; --i) { if(i-1 < 0 || Solution.lilypadForest[i-1] < Solution.lilypadForest[i]) { // console.log(" Traveled Left: ", index-i); return index-i; } } } } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { let forest1 = [5,7,3,1,2,3,4,6,2]; console.log("Forest 1: ", maxDistance(forest1)); // RUN: deno run Playground/Puzzles/AngryFrogs.ts } ================================================ FILE: Playground/ZTM Challenges/Arrays/Compare_Arrays.test.ts ================================================ import { assertEquals, assertNotEquals } from "../../../test_deps.ts"; import { containsCommonItem } from "./Compare_Arrays.ts"; const array1 = ['a', 'b', 'c', 'x']; const array2 = ['z', 'y', 'x']; const array3 = ['z', 't', 'i']; //--------------------------------------------------------------------- // ---------- UNIT TESTS ---------- //--------------------------------------------------------------------- // RUN: deno test Playground/Challenges/Arrays/Compare_Arrays.test.ts Deno.test({ name: "Matching Elements 1", fn() { assertEquals(containsCommonItem(array1, array2) , true); } }); Deno.test({ name: "Matching Elements 2", fn() { assertEquals(containsCommonItem(array2, array3) , true); } }); Deno.test({ name: "No Matching 1", fn() { assertNotEquals(containsCommonItem(array1, array3) , true); } }); ================================================ FILE: Playground/ZTM Challenges/Arrays/Compare_Arrays.ts ================================================ // Given 2 arrays, create a function that let's a user // know (true/false) whether these two arrays contain any common items // // For Example: // const array1 = ['a', 'b', 'c', 'x']; // const array2 = ['z', 'y', 'i']; // Should return false. // ----------- // const array1 = ['a', 'b', 'c', 'x']; // const array2 = ['z', 'y', 'x']; // Should return true. // Arrays have no size limit // Return true or false // Naive (Brute Force) Solution function containsCommonItemBF(arr1: Array, arr2: Array): boolean { for (let i=0; i < arr1.length; i++) { // O(a) for (let j=0; j < arr2.length; j++) { // O(b) if(arr1[i] === arr2[j]) // O(1) return true; } } return false; } // Time Efficient Solution export function containsCommonItem(arr1: Array, arr2: Array): boolean { // Iterate through first array and record seen items let map = Object.create({}); for (let i=0; i < arr1.length; i++) { // O(a) const currentItem = arr1[i]; if (!map[currentItem]) { map[currentItem] = true; } } // Iterate through second array and check the record for (let i=0; i < arr2.length; i++) { // O(b) if (map[arr2[i]]) return true; } return false; } // Works the same as time efficient solution export function containsCommonItemSyntacticSugar(arr1: Array, arr2: Array): boolean { return arr1.some(item => arr2.includes(item)); } if (import.meta.main) { const array1 = ['a', 'b', 'c', 'x']; const array2 = ['z', 'y', 'x']; const array3 = ['z', 'y', 'i']; console.log(containsCommonItemBF(array1, array2)); // O(a*b) console.log(containsCommonItemBF(array1, array3)); // O(a*b) console.log(containsCommonItem(array1, array2)); // O(a+b) console.log(containsCommonItem(array1, array3)); // O(a+b) } ================================================ FILE: Playground/ZTM Challenges/Arrays/Merge_Sorted_Arrays.ts ================================================ // Challenge: Given two sorted numerical arrays, // merge them into a sorted array // // Example: // Input: [1,3,8,9] and [2,5,7] // Output: [1,2,3,5,7,8,9] function mergeSortedArrays(arr1: number[], arr2: number[]): Array { // Time Complexity: O(a+b) const largeSorted: number[] = []; for(let i=0,j=0; i <= arr1.length && j <= arr2.length;) { if (i === arr1.length && j === arr2.length) { break; } if (i === arr1.length) { largeSorted.push(arr2[j]); ++j; continue; } if (j === arr2.length) { largeSorted.push(arr1[i]); ++i; continue; } if (arr1[i] < arr2[j]) { largeSorted.push(arr1[i]); ++i; } else { largeSorted.push(arr2[j]) ++j; } } return largeSorted; } const arr1 = [1,3,8,9]; const arr2 = [2,5,7]; console.log(mergeSortedArrays(arr1, arr2)); // O(a+b) console.log(mergeSortedArrays([4,6,30], [0,3,4,31])); // O(a+b) console.log(mergeSortedArrays([2,4,8,16,32], [])); // O(a+b) console.log(mergeSortedArrays([], [])); // O(a+b) ================================================ FILE: Playground/ZTM Challenges/Arrays/Reverse_String.ts ================================================ // Challenge: Reverse the characters of a string, preserving // capitalization and spaces // // Example: // Input — "Avatar the Last Airbender" // Output — "rednebriA tsaL eht ratavA" function reverseString(input: string): string { let reversed: string[] = []; if (input.length > 1) { for (let i = input.length; i > 0; --i) { reversed.push(input.charAt(i-1)); // input[i-1] will also work in JS } } return reversed.join(''); } function reverseWithJavascriptVoodoo(input: string): string { return input.split('').reverse().join(''); } const reverseWithMoreVoodoo = (str: string) => [...str].reverse().join(''); console.log(reverseString("ATLA")); console.log(reverseString("Avatar the Last Airbender")); ================================================ FILE: Playground/ZTM Challenges/Arrays/Sum_Pair.ts ================================================ // Challenge: Given an array of unordered numbers and a sum, // is there a pair of numbers which add up to the sum? // Naive (Brute Force) Solution function hasPairWithSumBF(arr: number[], sum: number): boolean { for (var i = 0; i < arr.length - 1; i++) { for (var j = i + 1; j < arr.length; j++) { if (arr[i] + arr[j] === sum) { return true; } } } return false; } // Efficient Solution function hasPairWithSum(arr: number[], sum: number) { const mySet = new Set(); for (let i = 0; i < arr.length; i++) { if (mySet.has(arr[i])) { return true; } mySet.add(sum - arr[i]); } return false; } console.log(hasPairWithSumBF([6, 4, 3, 2, 1, 7], 9)); // O(n^2) console.log(hasPairWithSum([6, 4, 3, 2, 1, 7], 9)); // O(n) console.log(hasPairWithSum([1,2,3,9], 8)); // O(n) ================================================ FILE: Playground/ZTM Challenges/Hash-Tables/Recurring_Symbol.test.ts ================================================ import { assertEquals } from "../../../test_deps.ts"; import { firstRecurringNumber, findFirstRecurring, } from "./Recurring_Symbol.ts"; const array1 = [2, 5, 1, 2, 3, 5, 1, 2, 4]; const array2 = [2, 1, 1, 2, 3, 5, 1, 2, 4]; const array3 = [2, 3, 4, 5]; const array4 = [2, 5, 5, 2, 3, 5, 1, 2, 4]; const array5 = ["a", "z", "n", "z", "x", "g"]; //--------------------------------------------------------------------- // ---------- UNIT TESTS ---------- //--------------------------------------------------------------------- // RUN: deno test Playground/Challenges/Hash-Tables/Recurring_Symbol.test.ts Deno.test({ name: "Recurring Character", fn() { // ["a", "z", "n", "z", "x", "g"]; assertEquals(findFirstRecurring(array5), "z"); } }); Deno.test({ name: "Recurring Number 1", fn() { // [2, 5, 1, 2, 3, 5, 1, 2, 4] assertEquals(firstRecurringNumber(array1), 2); } }); Deno.test({ name: "Recurring Number 2", fn() { // [2, 1, 1, 2, 3, 5, 1, 2, 4] assertEquals(firstRecurringNumber(array2), 1); } }); Deno.test({ name: "Recurring Number 3", fn() { // [2, 3, 4, 5] assertEquals(firstRecurringNumber(array3), undefined); } }); Deno.test({ name: "Recurring Number 4", fn() { // [2, 5, 5, 2, 3, 5, 1, 2, 4] assertEquals(firstRecurringNumber(array4), 5); } }); ================================================ FILE: Playground/ZTM Challenges/Hash-Tables/Recurring_Symbol.ts ================================================ // ---------- Google Interview Question ---------- // Given an array = [2,5,1,2,3,5,1,2,4]: // It should return 2 // Given an array = [2,1,1,2,3,5,1,2,4]: // It should return 1 // Given an array = [2,3,4,5]: // It should return undefined // Bonus... What if we had this: // [2,5,5,2,3,5,1,2,4] // Return 5 because the pairs are before 2,2 // Solution by CoffeelessProgrammer --------------------------------------- /** * Finds the first recurring element of a numerical array. * @param {Array} numArray * @returns {number | undefined} First repeated number, or undefined if none */ export function firstRecurringNumber(numArray: number[]): number | undefined { let seen = new Set(); for (let i=0; i < numArray.length; ++i) { if (seen.has(numArray[i])) { return numArray[i]; } seen.add(numArray[i]); } return undefined; } /** * Finds the first recurring element of a numerical or string array. * @param {Array | Array} arr * @returns {number | string | undefined} First repeated symbol, or undefined if none */ export function findFirstRecurring(arr: number[] | string[]): number | string | undefined { let seen = new Set(); for (let i=0; i < arr.length; ++i) { if (seen.has(arr[i])) { return arr[i]; } seen.add(arr[i]); } return undefined; } ================================================ FILE: Playground/ZTM Challenges/Josephus.ts ================================================ export default class Josephus { private numberOfSoldiers: number; private circleOfSoldiers: Array; private livingCount: number; constructor(numSoldiers: number) { this.numberOfSoldiers = numSoldiers > 1 ? numSoldiers : 2; this.circleOfSoldiers = new Array(this.numberOfSoldiers).fill(true); this.livingCount = this.numberOfSoldiers; } public setNumSoldiers(numSoldiers: number) { this.numberOfSoldiers = numSoldiers; this.reset(); } public getNumSoldiers(): number { return this.numberOfSoldiers; } public reset(): void { this.circleOfSoldiers = new Array(this.numberOfSoldiers).fill(true); this.livingCount = this.numberOfSoldiers; } public solveBF(): number { if (this.numberOfSoldiers < 2) return 0; let lastKnight: number = -1; let dead = Array(); for (let i=this._findThirdLiving(0); this.livingCount > 1; i = this._findThirdLiving(i)) { this._markDead(i) dead.push(i+1); i = this._findNextLiving(i); if (this.livingCount === 1) lastKnight = i; } this.reset(); return lastKnight; } public solveVisual(): number { if (this.numberOfSoldiers < 2) return 0; let lastKnight: number = -1; console.log('\nThe Romans prepare to clear the caves.\n'); let dead = Array(); for (let i=this._findThirdLiving(0); this.livingCount > 1; i = this._findThirdLiving(i)) { this._markDead(i) dead.push(i+1); console.log('\tKnight', i+1, 'is slain.'); i = this._findNextLiving(i); console.log('Knight', i+1, 'prepares to strike the next blow.') if (this.livingCount === 1) { console.log('But sees no one meet his gaze...'); lastKnight = i; } } console.log('\nK'+(lastKnight+1), 'is the last knight standing.'); console.log('\nSlain knights:', dead.toString()); this.reset(); return lastKnight; } private _findNextLiving(index: number): number { for (let i = (index+1)%this.numberOfSoldiers; this.livingCount>0; i = (i+1)%this.numberOfSoldiers) { if (this.circleOfSoldiers[i] === true) return i; } return -1; } private _findThirdLiving(index: number): number { let counter = 0; for (let i = (index+1)%this.numberOfSoldiers; this.livingCount>1; i = (i+1)%this.numberOfSoldiers) { if (this.circleOfSoldiers[i] === true) { if (counter < 3) ++counter; } if (counter === 3) return i; } return -1; } private _markDead(index:number): number { this.circleOfSoldiers[index] = false; --this.livingCount; return index; } } function printJosephusSolution(circleOfKnights: Josephus) { console.log('\nSolution for Circle of', circleOfKnights.getNumSoldiers(),'\n– Last Knight at Index:', circleOfKnights.solveBF()); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const knights1 = new Josephus(-1); knights1.solveVisual(); const knights2 = new Josephus(6); knights2.solveVisual(); const knights3 = new Josephus(8); knights3.solveVisual(); printJosephusSolution(knights3); // RUN: deno run Playground/Challenges/Josephus.ts } // --------------------------- Terminal Output: --------------------------- // The Romans prepare to clear the caves. // // Knight 2 is slain. // Knight 1 prepares to strike the next blow. // But sees no one meet his gaze... // // K1 is the last knight standing. // // Slain knights: 2 // // The Romans prepare to clear the caves. // // Knight 4 is slain. // Knight 5 prepares to strike the next blow. // Knight 2 is slain. // Knight 3 prepares to strike the next blow. // Knight 1 is slain. // Knight 3 prepares to strike the next blow. // Knight 3 is slain. // Knight 5 prepares to strike the next blow. // Knight 6 is slain. // Knight 5 prepares to strike the next blow. // But sees no one meet his gaze... // // K5 is the last knight standing. // // Slain knights: 4,2,1,3,6 // // The Romans prepare to clear the caves. // // Knight 4 is slain. // Knight 5 prepares to strike the next blow. // Knight 8 is slain. // Knight 1 prepares to strike the next blow. // Knight 5 is slain. // Knight 6 prepares to strike the next blow. // Knight 2 is slain. // Knight 3 prepares to strike the next blow. // Knight 1 is slain. // Knight 3 prepares to strike the next blow. // Knight 3 is slain. // Knight 6 prepares to strike the next blow. // Knight 7 is slain. // Knight 6 prepares to strike the next blow. // But sees no one meet his gaze... // // K6 is the last knight standing. // // Slain knights: 4,8,5,2,1,3,7 // // Solution for Circle of 8 // – Last Knight at Index: 5 ================================================ FILE: Playground/ZTM Challenges/Recursion/ReverseString.ts ================================================ function reverseStringRecursive(str: string): string { if (str.length===0) return ""; return reverseStringRecursive(str.substring(1)) + str.charAt(0); } function printReverseString(str: string) { console.log(str, '–>', reverseStringRecursive(str)); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { printReverseString("Hello World!"); printReverseString("C0mp!ex1tY"); printReverseString("Avatar: The Last Airbender"); // RUN: deno run Playground/Challenges/Recursion/ReverseString.ts } ================================================ FILE: Playground/ZTM Challenges/StackifiedQueue.ts ================================================ import Stack from '../../Data-Structures/Sequential/Stack.ts'; // Ahhhhh! What a glorious day to do things just because we can! ^.^ class StackifiedQueue { private first: Stack; // Stack with elements ordered first-on-top private last: Stack; // Stack with elements ordered last-on-top private length: number; constructor() { this.first = new Stack(); this.last = new Stack(); this.length = 0; } public getlength(): number { return this.length; } public isEmpty(): boolean { return this.length === 0; } public enqueue(value: T): boolean { // Transfer all items from first-on-top to last-on-top this._populateLastOnTop(); // Add to end of line, i.e. push to last-on-top stack this.last.push(value); ++this.length; return true; } public dequeue(): T | null { if(this.length === 0) return null; this._populateFirstOnTop(); --this.length; return this.first.pop(); } public peek(): T | null { if (this.length === 0) return null; this._populateFirstOnTop(); return this.first.peek(); } private _populateFirstOnTop(): void { if (this.first.getLength() > 0) return; for (let i = 0; i < this.length; ++i) { this.first.push(this.last.pop()); } } private _populateLastOnTop(): void { if (this.last.getLength() > 0) return; for (let i = 0; i < this.length; ++i) { this.last.push(this.first.pop()); } } } function printQueue(queue: StackifiedQueue) { console.log(JSON.stringify(queue)); } function printPeekQueue(queue: StackifiedQueue) { console.log('Peeking... Found', JSON.stringify(queue.peek())); } function printDequeue(queue: StackifiedQueue) { console.log('Dequeued:', JSON.stringify(queue.dequeue())); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const ATLA = new StackifiedQueue(); printPeekQueue(ATLA); ATLA.enqueue('Sokka'); ATLA.enqueue('Katara'); printPeekQueue(ATLA); ATLA.enqueue('Aang'); ATLA.enqueue('Appa'); printQueue(ATLA); printDequeue(ATLA); printDequeue(ATLA); printDequeue(ATLA); printQueue(ATLA); printDequeue(ATLA); printQueue(ATLA); ATLA.enqueue('Zuko'); ATLA.enqueue('Iroh'); printQueue(ATLA); // RUN: deno run Playground/Challenges/StackifiedQueue.ts } // --------------------------- Terminal Output: --------------------------- // Peeking... Found null // Peeking... Found "Sokka" // {"first":{"length":0,"values":[null,null]},"last":{"length":4,"values":["Sokka","Katara","Aang","Appa"]},"length":4} // Dequeued: "Sokka" // Dequeued: "Katara" // Dequeued: "Aang" // {"first":{"length":1,"values":["Appa",null,null,null]},"last":{"length":0,"values":[null,null,null,null]},"length":1} // Dequeued: "Appa" // {"first":{"length":0,"values":[null,null,null,null]},"last":{"length":0,"values":[null,null,null,null]},"length":0} // {"first":{"length":0,"values":[null,null,null,null]},"last":{"length":2,"values":["Zuko","Iroh",null,null]},"length":2} ================================================ FILE: Playground/ZTM Challenges/ValidatorBST.ts ================================================ import BST from '../../Data-Structures/Trees/BinarySearchTree.ts'; import Node from '../../Data-Structures/Trees/BinaryTreeNode.ts'; export default class BinarySearchTreeValidator { private static previousVal: number | undefined = undefined; private static _resetValidator() { this.previousVal = undefined; } public static validate(tree: BST, visualize?: boolean): boolean { const root = tree.getRoot(); if (!root) return false; visualize = visualize || false; const valid: boolean = this._traverseInOrder(root, visualize); this._resetValidator(); return valid; } private static _traverseInOrder(node: Node, visualize: boolean): boolean { if (node.hasLeft()) this._traverseInOrder(node.getLeft(), visualize); const currentValue = node.getValue(); if (visualize) console.log('\tCurrent Value:', node.getValue(), '\tPrevious:', this.previousVal); if (!this.previousVal) this.previousVal = currentValue; else if (currentValue < this.previousVal) return false; else this.previousVal = currentValue; if (node.hasRight()) this._traverseInOrder(node.getRight(), visualize); return true; } } function printBSTValidation(tree: BST, treeLabel: string) { console.log(treeLabel, '– Original:', BinarySearchTreeValidator.validate(tree, true)); } //--------------------------------------------------------------------- // ---------- MAIN PROGRAM ---------- //--------------------------------------------------------------------- if (import.meta.main) { const tree1 = new BST(); tree1.insert(9); tree1.insert(4); tree1.insert(6); tree1.insert(20); tree1.insert(170); tree1.insert(15); tree1.insert(1); // 9 // 4 20 // 1 6 15 170 printBSTValidation(tree1, 'Tree 1'); tree1.invertTree(); // 9 // 20 4 // 170 15 6 1 printBSTValidation(tree1, 'Tree 1'); const tree2 = new BST(); tree2.insert(16); tree2.insert(8); tree2.insert(32); printBSTValidation(tree2, 'Tree 2'); tree2.invertTree(); printBSTValidation(tree2, 'Tree 2'); const tree3 = new BST(); tree3.insert(48); tree3.insert(29); tree3.insert(77); tree3.insert(18); tree3.insert(16); tree3.insert(68); tree3.insert(72); tree3.insert(83); printBSTValidation(tree3, 'Tree 3'); tree3.invertTree(); printBSTValidation(tree3, 'Tree 3'); // RUN: deno run Playground/Challenges/ValidatorBST.ts } // --------------------------- Terminal Output: --------------------------- // Current Value: 1 Previous: undefined // Current Value: 4 Previous: 1 // Current Value: 6 Previous: 4 // Current Value: 9 Previous: 6 // Current Value: 15 Previous: 9 // Current Value: 20 Previous: 15 // Current Value: 170 Previous: 20 // Tree 1 – Original: true // Current Value: 170 Previous: undefined // Current Value: 20 Previous: 170 // Current Value: 9 Previous: 170 // Tree 1 – Original: false // Current Value: 8 Previous: undefined // Current Value: 16 Previous: 8 // Current Value: 32 Previous: 16 // Tree 2 – Original: true // Current Value: 32 Previous: undefined // Current Value: 16 Previous: 32 // Tree 2 – Original: false // Current Value: 16 Previous: undefined // Current Value: 18 Previous: 16 // Current Value: 29 Previous: 18 // Current Value: 48 Previous: 29 // Current Value: 68 Previous: 48 // Current Value: 72 Previous: 68 // Current Value: 77 Previous: 72 // Current Value: 83 Previous: 77 // Tree 3 – Original: true // Current Value: 83 Previous: undefined // Current Value: 77 Previous: 83 // Current Value: 48 Previous: 83 // Tree 3 – Original: false ================================================ FILE: README.md ================================================ # Data Structures & Algorithms in TypeScript ## Environment - Deno 1.12.0 - V8 9.2.230.14 - TypeScript 4.3.2 ## Topics Overview ### Algorithmic Complexity Analysis - [X] Big O Notation - [X] Time Complexity - [X] Space Complexity ### Data Structures - [X] Trees - [X] Graphs - [X] Hash Tables - [X] Linked Lists - [X] Arrays/Stacks/Queues ### Algorithms - [X] Comparison Sorting - [X] *Merge Sort* - [X] *Quicksort* - [X] Searching - [X] Linear & Binary Search - [X] BFS & DFS - [X] Recursion - [X] Dynamic Programming ## Resources - [Roadmap: Core Data Structures & Algorithms](https://coggle.it/diagram/W5E5tqYlrXvFJPsq/t/master-the-interview-click-here-for-course-link "Course and Mindmap by Andrei Neagoie") - [The Big-O Algorithm Complexity Cheat Sheet](https://www.bigocheatsheet.com/ "Big O Cheat Sheet") - [Algorithms & DS – Real Use Cases: PragmaticEngineer](https://blog.pragmaticengineer.com/data-structures-and-algorithms-i-actually-used-day-to-day/) ================================================ FILE: deps.ts ================================================ // ----------------------- Standard Library ----------------------- // ---------------------- Third-Party Modules ---------------------- ================================================ FILE: test_deps.ts ================================================ // ----------------------- Testing Library ----------------------- export { assertEquals, assertNotEquals, } from "https://deno.land/std@0.101.0/testing/asserts.ts"; // ------------------------ Miscellaneous ------------------------ import * as all_deps from "./deps.ts";