Progress
🌍
Why does this topic matter?
Java is the dominant language for FAANG backend systems and the de-facto language of coding interviews. Before you can write a single algorithm, you need to know how the computer stores data (types), how it makes decisions (control flow), and how it organizes code (methods and OOP). Every topic in this course — arrays, trees, graphs, dynamic programming — is built on top of these fundamentals. Skipping this is like trying to write poetry without knowing the alphabet.

📖 Before We Start — The Big Picture

Think of a computer program as a recipe. Variables are your ingredients, data types are the containers (you can't store a liquid in a bag or a powder in a bottle), control flow is the list of steps, and methods are reusable sub-recipes. The computer follows these instructions one at a time, storing and reading values from memory as it goes.

At the hardware level, everything is just 0s and 1s in memory. Java's type system gives those raw bits meaning — a 32-bit pattern means a very different thing when interpreted as an int vs a float. This lecture teaches you to pick the right container for the right data, every time.

🔗
How it connects: This is the starting point — no prerequisites. Lecture 2 builds directly on this by explaining where these variables physically live in memory (stack vs heap). Every later lecture assumes you are fluent in these fundamentals.

🧱 Data Types

1.1 — Primitive Types (8 total)

Stored directly on the stack. Fixed size, no null, ultra-fast access. These are the atoms of Java.

Type Bits Range Default DSA Usage
byte 8 −128 → 127 0 raw byte buffers, rarely in DSA
short 16 −32,768 → 32,767 0 rarely used
int 32 −2.1B → 2.1B 0 default integer — use this always
long 64 ±9.2 × 10¹⁸ 0L large products, timestamps, overflow-prone problems
float 32 ~6-7 sig. digits 0.0f avoid — precision issues
double 64 ~15-16 sig. digits 0.0 default decimal
char 16 0 → 65,535 (Unicode) \u0000 frequency arrays, char arithmetic
boolean 1 bit true / false false flags, visited arrays, conditions
💡
DSA Rule of Thumb for type choice:
• Default to int for all integers.
• Switch to long when result can exceed ~2.1 billion (e.g. n × m with n,m ≈ 10⁵).
char - 'a' gives you an index 0–25 — this is how you build O(1) frequency arrays for lowercase letters.
double for geometry, probabilities, averages.

1.2 — Reference (Non-Primitive) Types

Variables hold a memory address (reference) pointing to an object on the heap. Can be null. Includes all classes, arrays, interfaces.

☕ Java
// Primitive — value lives IN the variable (stack)
int x = 42;           // x IS 42

// Reference — variable holds ADDRESS pointing to heap
String s = "hello";  // s holds address of "hello" object on heap
int[] arr = {1,2,3};  // arr holds address of the array on heap

// Consequence: == checks identity (same address), not equality!
String a = new String("hi");
String b = new String("hi");
System.out.println(a == b);       // false  (different addresses!)
System.out.println(a.equals(b));  // true   (same content) ← USE THIS

1.3 — Type Casting (Interview Gotchas)

☕ Java · Casting Traps
// Widening (implicit, safe) — smaller → larger
int i = 100;
long l = i;          // auto-widens: no data loss
double d = i;        // auto-widens to 100.0

// Narrowing (explicit, lossy) — larger → smaller
double pi = 3.99;
int truncated = (int) pi;   // → 3  (TRUNCATES, does NOT round!)

// ⚠️ GOTCHA 1: integer division truncates
double wrong   = 5 / 2;      // → 2.0  (int division first, THEN widened)
double correct = 5.0 / 2;    // → 2.5  (double division)
double correct2 = (double) 5 / 2; // → 2.5  (cast first)

// ⚠️ GOTCHA 2: int overflow — use long!
int n = 100000;
int overflow = n * n;         // → negative! (wraps around 2.1B limit)
long safe = (long) n * n;    // → 10000000000L ✓

// ⭐ char arithmetic — essential for DSA
int idx = 'e' - 'a';            // → 4  (freq array index)
char c = (char)('a' + 3);       // → 'd'
boolean isLower = (ch >= 'a' && ch <= 'z'); // char range check
char upper = (char)(ch - 32);   // lowercase → uppercase
⚠️
Autoboxing / Unboxing Trap — Common Interview Gotcha!
Java caches Integer values from −128 to 127 (the Integer cache). Outside this range each auto-boxing creates a new heap object.

Integer a = 127, b = 127; a == btrue (same cached object)
Integer c = 128, d = 128; c == dfalse (two different heap objects!)

Rule: ALWAYS use .equals() to compare wrapper types — never ==.
Additional trap: Unboxing a null Integer to int silently throws NullPointerException!

🔁 Control Flow

2.1 — All Loop Types

🎯
Pattern Recognition: Which loop to use?
for: When you know the exact iteration count upfront.
while: When iteration count depends on a condition (unknown upfront). Classic for binary search, two-pointer, digit extraction.
do-while: When the body must execute at least once. Classic for menu loops, digit extraction where n=0 is possible.
for-each: Cleanest for read-only traversal of collections/arrays when you don't need the index.
☕ Java · All Loop Types + DSA Usage
// ── FOR loop — use when count is known ───────────────────────
for (int i = 0; i < n; i++)          // forward: i goes 0..n-1
for (int i = n-1; i >= 0; i--)        // reverse: suffix processing
for (int i = 0; i < n; i += 2)        // step 2: even indices
for (int i = 0; i < n-1; i++)         // n-1 pairs: adj comparison

// ── WHILE loop — use when count is unknown ───────────────────
while (lo <= hi) { ... }              // binary search template
while (n > 0) { digit = n%10; n/=10;} // digit extraction
while (n > 1) { n /= 2; count++; }   // count divisions → O(log n)
while (fast != null && fast.next != null) // linked list traversal

// ── DO-WHILE — body runs at least once ───────────────────────
do {
    digit = n % 10;
    reversed = reversed * 10 + digit;
    n /= 10;
} while (n != 0);                    // handles n=0 correctly

// ── FOR-EACH — read-only traversal ───────────────────────────
for (int x : arr) sum += x;           // array
for (String s : list) process(s);    // collection
for (Map.Entry<K,V> e : map.entrySet()) // map iteration

// ── NESTED LOOPS — know the complexity! ──────────────────────
for (int i = 0; i < n; i++)          // ┐ O(n²) — all pairs
    for (int j = i+1; j < n; j++) {}  // ┘ j starts at i+1: n(n-1)/2 pairs

// ── BREAK / CONTINUE ─────────────────────────────────────────
break;    // exits the INNERMOST loop only
continue; // skips to NEXT iteration of innermost loop
// To break an outer loop: use labeled break
outer: for (...) {
    for (...) { if (found) break outer; } // exits outer loop!
}

2.2 — Conditionals & Short-Circuit Evaluation

☕ Java · Conditionals
// Standard if-else
if (x > 0) { ... }
else if (x == 0) { ... }
else { ... }

// Ternary — for simple single-value selection
int max = (a > b) ? a : b;
int abs = (x < 0) ? -x : x;     // ternary absolute value

// Switch (Java 14+ — enhanced switch expression)
int days = switch (month) {
    case 1, 3, 5, 7, 8, 10, 12 -> 31;
    case 4, 6, 9, 11 -> 30;
    default -> 28;
};

// ⭐ Short-circuit evaluation — CRITICAL for null safety!
// && stops at first FALSE condition
if (node != null && node.val == target) { ... }
// if node==null, right side NEVER evaluates → no NullPointerException

// || stops at first TRUE condition
if (arr == null || arr.length == 0) return;
// if arr==null, second check NEVER runs → safe!

// ⚠️ Integer.MAX_VALUE and MIN_VALUE — essential constants
int max = Integer.MAX_VALUE;  // 2^31 - 1 = 2,147,483,647
int min = Integer.MIN_VALUE;  // -2^31  = -2,147,483,648
int res = Math.max(a, b);      // Math utility methods
int abs = Math.abs(x);
double sq = Math.sqrt(n);

🔧 Methods & Arrays

3.1 — Method Anatomy

☕ Java · Method Anatomy
//  access    static   return   name          parameters
public static   int   binarySearch(int[] arr, int target) {
    // body
    return -1;  // must return int (matches return type)
}

// void methods — no return value (but can still return early!)
public static void swap(int[] arr, int i, int j) {
    int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
}

// Method overloading — same name, different params
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; } // different return type too

// Varargs — variable number of args (used in DSA for flexibility)
int sum(int... nums) {
    int total = 0;
    for (int n : nums) total += n;
    return total;
}
// call: sum(1), sum(1,2), sum(1,2,3,4,5) — all valid

3.2 — Pass-by-Value: The Most Misunderstood Thing in Java

⚠️
Java is ALWAYS pass-by-value. No exceptions.
The confusion: for objects and arrays, the VALUE being passed is the reference (address).
A copy of the address is passed → mutations through that address persist. But reassigning the reference inside the method doesn't affect the caller.
☕ Java · Pass-by-Value Illustrated
// PRIMITIVE: copy of value — original unchanged
void tryChange(int x) { x = 99; }
int a = 5;
tryChange(a);
System.out.println(a);  // still 5 — x was a COPY

// ARRAY: copy of reference — mutations visible to caller!
void mutate(int[] arr) { arr[0] = 99; }
int[] arr = {1,2,3};
mutate(arr);
System.out.println(arr[0]);  // 99 — mutation persisted!

// REFERENCE REASSIGNMENT: doesn't affect caller
void reassign(int[] arr) { arr = new int[]{9,9,9}; }
reassign(arr);
System.out.println(arr[0]);  // still 99 — local arr was a copy of address

// ⭐ Consequence: swap(int a, int b) NEVER works
//   swap(int[] arr, int i, int j) DOES work (mutations on shared array)

3.3 — Arrays: Your #1 DSA Tool

☕ Java · Arrays — Complete Reference
// ── Declaration & Initialization ─────────────────────────────
int[] a = new int[5];          // [0,0,0,0,0] — auto-initialized to 0
boolean[] vis = new boolean[n]; // [false,false,...] auto init
int[] b = {3, 1, 4, 1, 5};     // literal init
int[][] mat = new int[3][4];   // 3 rows, 4 cols — all 0
int[][] grid = {{1,2},{3,4}};  // 2D literal

// ── Access ────────────────────────────────────────────────────
a.length                        // FIELD (no parens!) — O(1)
mat.length                      // number of rows
mat[0].length                   // number of cols in row 0

// ── java.util.Arrays methods ──────────────────────────────────
Arrays.sort(a);                 // O(n log n) — dual-pivot quicksort
Arrays.sort(a, 0, 5);          // sort subarray [0,5)
Arrays.fill(a, -1);             // fill all with -1  O(n)
Arrays.fill(a, 2, 5, 0);       // fill range [2,5) with 0
Arrays.copyOf(a, a.length);    // full copy — O(n)
Arrays.copyOfRange(a, 1, 4);   // subarray copy — indices [1,4)
Arrays.equals(a, b);           // element-wise comparison
Arrays.binarySearch(a, target); // O(log n) — array must be sorted!
Arrays.toString(a);            // "[1, 2, 3]" for printing

// ── Custom sort with Comparator ───────────────────────────────
Integer[] arr = {3,1,4,1,5};
Arrays.sort(arr, (a, b) -> b - a); // descending — note: needs Integer[], not int[]
// Sort by absolute value:
Arrays.sort(arr, (a, b) -> Math.abs(a) - Math.abs(b));

📦 OOP Fundamentals

4.1 — The Four Pillars (with DSA relevance)

Pillar What it means DSA example
Encapsulation Bundle data + methods; hide internals LinkedList class with private head, public add/remove
Abstraction Expose what, hide how PriorityQueue — you call offer/poll, not heapifyUp
Inheritance Subclass extends superclass, reuses code TreeNode extends Node; DFS/BFS methods inherited
Polymorphism Same method, different behaviour Comparator — sort by different keys without changing sort logic
☕ Java · TreeNode (Used in 90% of Tree Problems)
// The class you'll write/use constantly in tree problems
class TreeNode {
    int val;
    TreeNode left, right;

    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val; this.left = left; this.right = right;
    }
}

// ListNode — for linked list problems
class ListNode {
    int val;
    ListNode next;
    ListNode(int val) { this.val = val; }
}

4.2 — Interfaces, Comparators & Generics

☕ Java · Comparator Patterns for DSA
// Custom sorting — appears in interval, scheduling, and K-th problems

// Sort intervals by start time
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

// Sort strings by length
Arrays.sort(words, (a, b) -> a.length() - b.length());

// Max-heap (reverse natural order) — used constantly!
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a,b) -> b-a);

// Min-heap by second element of pair
PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) -> a[1] - b[1]);

// Generics — type-safe collections
List<Integer> list = new ArrayList<>();
Map<String, List<Integer>> graph = new HashMap<>();
Set<Integer> visited = new HashSet<>();

4.3 — static & final Keywords

☕ Java · static & final
// static — belongs to the CLASS, shared across all instances
class Counter {
    static int count = 0;   // shared across ALL instances
    int id;                  // unique per instance
    Counter() { id = ++count; }
}

// static methods can't access instance fields (no 'this')
// Why main() is static: JVM calls it without creating an object

// final — prevents reassignment / inheritance / override
final int MAX = 100;        // constant; convention: ALL_CAPS
final String s = "hi"; s = "bye";  // ✗ COMPILE ERROR

// DSA constants you'll use constantly:
static final int INF = Integer.MAX_VALUE;  // Dijkstra, DP
static final int MOD = 1_000_000_007;       // modular arithmetic

4.4 — The equals() + hashCode() Contract

⚠️
The Contract: If a.equals(b) is true, then a.hashCode() == b.hashCode() MUST also be true.
Breaking this contract causes silent bugs in HashMap, HashSet, and every hash-based structure.
When using a custom object as a HashMap key, you MUST override both methods together.
☕ Java · Custom HashMap Key
class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Point)) return false;
        Point p = (Point) o;
        return x == p.x && y == p.y;
    }

    @Override
    public int hashCode() {
        return 31 * x + y;  // must be consistent with equals()
    }
}
// Without overriding: map.get(new Point(1,2)) → null even after put!
Map<Point, String> map = new HashMap<>();
map.put(new Point(1,2), "origin");
map.get(new Point(1,2));  // → "origin" ✓ (with both overrides)

⏱ Big-O Complexity Analysis

📌
What is Big-O?
Big-O is an upper bound on the growth rate of an algorithm's resource usage (time or space) as input size n → ∞. It describes how much slower your algorithm gets when you double the input — not the exact running time in milliseconds.

5.1 — The 4 Simplification Rules

# Rule Example Simplifies to
1 Drop constants O(3n + 500) O(n)
2 Drop non-dominant terms O(n² + n + log n) O(n²)
3 Different inputs → different variables Two arrays of size a and b processed sequentially O(a + b), NOT O(n)
4 Nested loops on same input → multiply Outer O(n), inner O(n) O(n²)

5.2 — Complexity Recognition by Code Pattern

☕ Java · Read Code → Know Complexity
// ── O(1) — fixed number of operations ────────────────────────
arr[i];                          // array access
map.get(key);                    // hashmap lookup
pq.peek();                       // heap peek

// ── O(log n) — input HALVED each step ────────────────────────
while (n > 1) { n /= 2; }      // log₂n iterations
// Binary search, heap operations, balanced BST ops

// ── O(√n) — loop to square root ──────────────────────────────
for (int i = 2; i*i <= n; i++) // prime check — √n iterations

// ── O(n) — single pass ───────────────────────────────────────
for (int i = 0; i < n; i++) {}  // n iterations
// Two-pointer, sliding window (both pointers move ≤ n times total)

// ── O(n log n) — sort, or divide + linear merge ──────────────
Arrays.sort(arr);               // O(n log n)
// Merge sort: log n levels × O(n) merge = O(n log n)

// ── O(n²) — nested loops over same input ─────────────────────
for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++) // always O(n²) even if j=i+1

// ── O(2ⁿ) — two recursive calls or all subsets ───────────────
// fib(n-1) + fib(n-2), or all 2ⁿ subsets of an array

// ── O(n!) — all permutations ─────────────────────────────────
// Generating all arrangements of n elements
Big-O n=10 n=100 n=1,000 n=10,000 Verdict
O(1) 1 1 1 1 🟢 Perfect
O(log n) 3 7 10 13 🟢 Excellent
O(√n) 3 10 32 100 🟢 Good
O(n) 10 100 1,000 10,000 🟢 Good
O(n log n) 33 664 10,000 133,000 🟡 Acceptable
O(n²) 100 10,000 1M 100M (slow) 🔴 Avoid for n>10⁴
O(2ⁿ) 1,024 10³⁰ 💀 🔴 Only for n≤20
O(n!) 3.6M 💀 🔴 Only for n≤10

5.2.5 — Input Constraints → Expected Complexity

🎯
The #1 Interview Meta-Skill: Read the constraint before writing code. Problem setters design the limits to hint at the expected algorithm's complexity.
Input Size (n) Max Complexity Typical Algorithms
n ≤ 10 O(n!) or O(nⁿ) Permutations, brute-force
n ≤ 20 O(2ⁿ) Backtracking, bitmask DP
n ≤ 500 O(n³) 3D DP, Floyd-Warshall
n ≤ 5,000 O(n²) DP tables, O(n²) sorts
n ≤ 10⁵ O(n log n) Merge sort, heap, segment tree
n ≤ 10⁶ O(n) Linear scan, two pointers, hashing
n ≤ 10⁸ O(n) tight constant Optimised linear, bitwise ops
n ≤ 10¹⁸ O(log n) Binary search, fast exponentiation

5.3 — Space Complexity

📐
Always report BOTH time and space in interviews.
Then ask: "Can I reduce space to O(1) using in-place manipulation?"
Two-pointers, in-place reversal, and bit manipulation are classic O(1) space techniques.
☕ Java · Space Complexity Examples
// O(1) space — only fixed number of extra variables
int sum = 0;
for (int x : arr) sum += x;   // one variable, regardless of n

// O(n) space — proportional to input
int[] copy = Arrays.copyOf(arr, n);   // n-size extra array
Map<?,?> map = new HashMap<>();      // up to n entries

// O(n) space — RECURSION! Each call frame is on the stack
int factorial(int n) {
    if(n==0) return 1;
    return n * factorial(n-1);  // depth n → O(n) stack space
}

// O(log n) space — binary search (recursive) depth = log n
// O(n) space  — merge sort call stack depth = log n, but merge arrays = O(n)

// Auxiliary space = extra space beyond input storage
// Total space = input + auxiliary

🗂 Java Collections Cheat Sheet

Memorize this table. Every data structure lecture will use these.

Collection Key Operations & Time When to use Important gotchas
int[] / array get/set: O(1), length: O(1) Fixed-size, primitives, frequency arrays Fixed size, no built-in methods
ArrayList<T> get: O(1), add: O(1)*, remove(i): O(n) Dynamic array, ordered, indexed access *amortized; remove by index is O(n)
LinkedList<T> add/remove ends: O(1), get(i): O(n) Deque operations, frequent inserts at ends Not cache-friendly; prefer ArrayDeque for stack/queue
HashMap<K,V> get/put/contains: O(1) avg, O(n) worst Frequency count, complement lookup, memoization Unordered; worst case O(n) if hash collisions
LinkedHashMap<K,V> Same as HashMap LRU Cache (preserves insertion order) Slightly slower than HashMap
TreeMap<K,V> get/put/floor/ceil: O(log n) Need sorted keys, floor/ceil queries Keys must be Comparable; Red-Black tree internally
HashSet<T> add/contains/remove: O(1) avg De-dup, O(1) lookup, visited set No ordering; no duplicates
TreeSet<T> add/contains/floor/ceil: O(log n) Sorted set, range queries Keys must be Comparable
PriorityQueue<T> offer/poll: O(log n), peek: O(1) Top-K, Dijkstra, median stream Min-heap by default; use (a,b)->b-a for max-heap
ArrayDeque<T> push/pop/peek (both ends): O(1) Stack, queue, sliding window (deque) Faster than Stack class; preferred in all DSA
Stack<T> push/pop/peek: O(1) Legacy; use ArrayDeque instead Synchronized (slow); avoid in competitive coding
🌲
TreeMap — Missing from Most Courses: When you need range or nearest-key queries in O(log n).
tm.floorKey(x) — largest key ≤ x  |  tm.ceilingKey(x) — smallest key ≥ x
tm.lowerKey(x) — largest key < x  |  tm.higherKey(x) — smallest key > x
tm.firstKey() / tm.lastKey() — min / max key
tm.headMap(x) — submap with all keys < x  |  tm.tailMap(x) — all keys ≥ x
Use for: Calendar events, sliding window problems, frequency-range queries.
☕ Java · Collections Quick Reference
// ── ArrayList ─────────────────────────────────────────────────
List<Integer> list = new ArrayList<>();
list.add(5);               // append
list.add(0, 5);            // insert at index 0 — O(n)!
list.get(i);               // O(1)
list.remove(i);            // by index — O(n); shifts everything
list.remove(Integer.valueOf(5)); // by value — O(n)
list.size(); list.isEmpty(); list.contains(x);
Collections.sort(list);    // O(n log n)
Collections.reverse(list);

// ── HashMap ───────────────────────────────────────────────────
Map<String,Integer> map = new HashMap<>();
map.put("a", 1);
map.get("a");                        // returns null if absent
map.getOrDefault("a", 0);           // ← use this to avoid null checks!
map.containsKey("a"); map.containsValue(1);
map.put("a", map.getOrDefault("a",0)+1); // frequency count pattern
map.putIfAbsent("a", new ArrayList<>()); // graph adjacency list
for (var e : map.entrySet())          // iterate all entries
    System.out.println(e.getKey() + " → " + e.getValue());

// ── PriorityQueue ─────────────────────────────────────────────
PriorityQueue<Integer> minPQ = new PriorityQueue<>();    // min-heap
PriorityQueue<Integer> maxPQ = new PriorityQueue<>(Collections.reverseOrder());
minPQ.offer(5);  // add
minPQ.peek();    // view min without removing — O(1)
minPQ.poll();    // remove and return min — O(log n)

// ── ArrayDeque as Stack ──────────────────────────────────────
Deque<Integer> stack = new ArrayDeque<>();
stack.push(5);   // push to front
stack.pop();     // remove from front
stack.peek();    // view front without removing

// ── ArrayDeque as Queue ──────────────────────────────────────
Deque<Integer> queue = new ArrayDeque<>();
queue.offer(5);  // enqueue at back
queue.poll();    // dequeue from front
queue.peek();    // view front

🔤 String Essentials for DSA

Java strings are immutable — every modification creates a new string. This is the #1 interview trap for beginners.

🚨
DANGER: String concatenation in a loop is O(n²)!
String s = ""; for(int i=0; i<n; i++) s += "a";
Each += creates a new string and copies everything. Use StringBuilder instead.
StringBuilder sb = new StringBuilder(); for(...) sb.append("a"); return sb.toString();

String Operations You Must Know

Operation Code Time Notes
Length s.length() O(1) Note: () for String, not .length
Get char s.charAt(i) O(1) Returns char, not String
Substring s.substring(i, j) O(j-i) Creates new string [i, j)
Compare s.equals(t) O(n) NEVER use == for strings!
To char array s.toCharArray() O(n) Convert to mutable array
Split s.split(" ") O(n) Returns String[]
Trim s.trim() O(n) Remove leading/trailing spaces
Contains s.contains("ab") O(n×m) Naive search
Index of s.indexOf("ab") O(n×m) Returns -1 if not found
To lowercase s.toLowerCase() O(n) Creates new string
💡
StringBuilder Cheat Sheet:
StringBuilder sb = new StringBuilder();
sb.append("hello"); — O(1) amortized
sb.insert(0, "x"); — O(n) — shifts everything
sb.reverse(); — O(n)
sb.toString(); — O(n) — get final String
Rule: If you're building a string character by character, ALWAYS use StringBuilder.

Common String Patterns in Interviews

  1. Two Pointers: Palindrome check, reverse string → LC 125
  2. Frequency Count: Anagram check, permutation in string → LC 242
  3. Sliding Window: Longest substring without repeats → LC 3
  4. HashMap: Group anagrams → LC 49
  5. Stack: Valid parentheses → LC 20

💪 In-Lecture Practice Problems

These are solved during the lecture. Think first, attempt on paper, then check solution.

Problem 01 · Brute → Optimize
Check if a Number is Prime
Easy GoogleAmazonMicrosoft O(√n) Optimization
Problem 02 · Two Pointers Preview
Reverse an Array In-Place
Easy AmazonMicrosoftFacebook Two Pointers
Problem 03 · Binary Search Template
Search in Sorted Array
Easy GoogleAmazonMicrosoft Binary Search LC #704
Problem 04 · HashMap Pattern
Two Sum
Easy GoogleAmazonFacebook MicrosoftAppleUber Complement Lookup LC #1
Problem 05 · Complexity Analysis
Maximum Subarray Sum (Kadane's Algorithm)
Medium GoogleAmazonFacebook MicrosoftApple Kadane's Algorithm LC #53
Problem 06 · HashSet Pattern
Contains Duplicate
Easy AmazonGoogleMicrosoft HashSet
Problem 07 · Frequency Counting
Valid Anagram
Easy AmazonGoogleUber Frequency Count
Problem 08 · Prefix Sum Introduction
Running Sum of 1D Array
Easy Amazon Prefix Sum
Problem 09 · Bit Manipulation Preview
Single Number (XOR Trick)
Easy AmazonGoogleApple XOR / Bit Manipulation
Problem 10 · Greedy / One-Pass
Best Time to Buy and Sell Stock
Easy AmazonFacebookGoogle Track Min/Max

📝 Assignment

📋
Lecture 1 Assignment — 27 Problems
Complete the assignment before moving to Lecture 2. It includes 8 Easy, 7 Medium, 5 Hard problems, 4 complexity exercises, and 7 bonus problems — all with LeetCode links.

📄 Open Assignment →

✅ Lecture Completion Checklist

Check each item before advancing to Lecture 2.

I can name all 8 primitive types, their sizes, and default values from memory
I know why 5/2 == 2 in Java and how to fix it
I understand pass-by-value: why swap(int a, int b) doesn't work but swap(int[] arr, i, j) does
I know when to use for vs while vs do-while
I can apply all 4 Big-O simplification rules to any code snippet
I recognize O(log n) when I see "divide by 2 each step"
I know the time complexity of HashMap.get(), PriorityQueue.poll(), TreeMap.get()
I can code Two Sum using HashMap in under 5 minutes
I can explain Kadane's logic (not just code) out loud
I've read the full Collections table and know which to use when
🧠
You're ready for Topic 2: Java Memory Management
Don't just write code; command memory. Understanding the JVM Stack and Heap is the difference between a senior engineer and a hobbyist. Master this foundation before we move to OOP and complex data structures.