Code-Alongs - Web Unit 3 Sprint 12

Code-Along Overview

In these code-along sessions, you'll work through practical examples and implementations of the concepts covered in the modules. These hands-on experiences will reinforce your learning and provide you with real-world problem-solving skills essential for technical interviews and professional development.

Code-Along Sessions

1. Big O Analysis and Caching

Learn how to analyze algorithm complexity and implement caching strategies through practical examples.

Key Concepts Covered:

  • Identifying time and space complexity of algorithms
  • Optimizing performance through caching
  • Implementing memoization for recursive functions
  • Comparing algorithms with different complexity classes

Code Example - Fibonacci with Caching:

// Naive recursive implementation - O(2^n) time complexity
function fibonacciSlow(n) {
  if (n <= 1) return n;
  return fibonacciSlow(n - 1) + fibonacciSlow(n - 2);
}

// Optimized implementation with caching - O(n) time complexity
function fibonacciFast(n, memo = {}) {
  // Check if we've already calculated this value
  if (n in memo) return memo[n];
  
  // Base cases
  if (n <= 1) return n;
  
  // Calculate and cache the result
  memo[n] = fibonacciFast(n-1, memo) + fibonacciFast(n-2, memo);
  return memo[n];
}

// Performance comparison
console.time('Slow Fibonacci');
console.log(fibonacciSlow(30)); // Very slow!
console.timeEnd('Slow Fibonacci');

console.time('Fast Fibonacci');
console.log(fibonacciFast(30)); // Much faster!
console.timeEnd('Fast Fibonacci');
                        

2. Linked Lists Implementation

Follow along as we implement a linked list data structure from scratch and explore common operations.

Key Concepts Covered:

  • Understanding linked list structure and properties
  • Implementing node creation and linking
  • Adding, removing, and finding elements
  • Traversing a linked list
  • Comparing linked lists with arrays

Code Example - Linked List Implementation:

class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
  }
}

class LinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }
  
  append(value) {
    const newNode = new Node(value);
    
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      this.tail.next = newNode;
      this.tail = newNode;
    }
    
    this.length++;
    return this;
  }
  
  prepend(value) {
    const newNode = new Node(value);
    
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      newNode.next = this.head;
      this.head = newNode;
    }
    
    this.length++;
    return this;
  }
  
  delete(value) {
    if (!this.head) return null;
    
    if (this.head.value === value) {
      this.head = this.head.next;
      this.length--;
      if (this.length === 0) {
        this.tail = null;
      }
      return true;
    }
    
    let current = this.head;
    while (current.next && current.next.value !== value) {
      current = current.next;
    }
    
    if (current.next) {
      if (current.next === this.tail) {
        this.tail = current;
      }
      current.next = current.next.next;
      this.length--;
      return true;
    }
    
    return false;
  }
  
  find(value) {
    if (!this.head) return null;
    
    let current = this.head;
    while (current) {
      if (current.value === value) {
        return current;
      }
      current = current.next;
    }
    
    return null;
  }
  
  toArray() {
    const array = [];
    let current = this.head;
    
    while (current) {
      array.push(current.value);
      current = current.next;
    }
    
    return array;
  }
}

// Usage example
const list = new LinkedList();
list.append(10).append(20).append(30).prepend(5);
console.log(list.toArray()); // [5, 10, 20, 30]
list.delete(20);
console.log(list.toArray()); // [5, 10, 30]
console.log(list.find(10)); // Node { value: 10, next: Node }