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