03

OOP for DSA & Design Rounds

Nested Node classes, constructors, equals/hashCode, records, generics and enums: just enough OOP to write clean DSA code and pass design rounds.

beginner~60 min3 simulations3 problems

▶ guided lesson⚡ 1-minute summary↻ 14 recall cards

before this: Java Memory

01 · why it exists

Why this exists, and the weight it carries

since1962–1967

Classes, objects & encapsulation

Ole-Johan Dahl and Kristen Nygaard

Dahl and Nygaard, at the Norwegian Computing Center, wanted to describe and simulate real systems. Simula I, a simulation language, and Simula 67, a general-purpose language built on ALGOL 60, introduced classes, objects and inheritance. In 2001 they received the ACM Turing Award "for ideas fundamental to the emergence of object oriented programming". Java's class, new and extends are those same three ideas.

the problem it solvedKeep each piece of state together with the only code allowed to change it, and create as many independent copies (objects) as the program needs.

its jobs today

  • Every design-round answer: a class whose fields hold the state and whose methods are the allowed operations.
  • Every node-based structure: ListNode, TreeNode, TrieNode and graph nodes are objects linked by references.
  • Encapsulation protects invariants: a ParkingSystem can never drop below 0 free slots, because only addCar changes the count.

the weight it carries

Illustrative: Design Parking System allows up to 1,000 slots per size and 1,000 calls. Modelling every slot as a flag stores 3,000 booleans and can scan 1,000 of them per call: up to 1,000 × 1,000 = 10⁶ checks. Storing only what the operations need is 3 ints and 1 check per call: 1,000 checks in total.

without it

State and the code that guards it drift apart: any line could set a count to −1, and design answers become bags of global variables.

when · why · how

when
Any "Design / Implement the X class" problem, and every node-based structure.
why
Grouping state with the methods that change it keeps every operation small, O(1) to reason about, and impossible to bypass.
how
private fields for the state, a constructor that builds a valid object, one public method per operation, and instance fields (not static) for anything per object.

02 · plain english

The idea in 30 seconds

A class is a blueprint; new builds an object from it on the heap. Every linked list and tree you write is objects pointing at objects.

Design questions ("Design a HashSet", "Design a Parking System") test one habit: keep the state in private fields, and make each operation a small method that checks, updates and returns.

HashMap, HashSet and sorting trust your class's equals, hashCode and compareTo. Keep those contracts and the library does the heavy lifting.

picture this

A Google Form: the form (class) defines the questions once, and every submission (object) holds its own answers. A setting printed on the form itself, like the closing date, is shared by every submission: that is a static field.

03 · remember

What you must know

  • Class vs object: a field without static exists once per object; a static field exists once per class, shared by every object.
  • Constructors build a valid object: this.size = size copies the parameter into the field, this(...) reuses another constructor, and final fields are set exactly once.
  • Encapsulation: private fields, changed only through methods that check their input. In design rounds, invalid calls return false or -1 exactly as the statement says.
  • `private static class Node` inside your class: no hidden link to an outer object, so nodes stay small and can be created from anywhere. Drop static only when the nested object must read the outer object's fields (an iterator).
  • LeetCode's `ListNode` / `TreeNode` are plain classes with fields val, next, left, right and three constructors. They are already defined for you: never redeclare them.
  • Interfaces say what (Deque, Map, Comparable, Iterator); classes say how. Declare by interface: Deque<Integer> st = new ArrayDeque<>();.
  • equals/hashCode contract: equal objects must have equal hash codes. A HashMap uses hashCode to pick the bucket and equals to confirm. Override both or neither.
  • Records (Java 16+) give a constructor, accessors, equals, hashCode and toString in one line: ideal for pairs, grid cells and HashMap keys.
  • Generics (List<T>, <T extends Comparable<T>>) write code once for every object type. Enums give named, type-safe constants (directions, states) that can carry data.
  • Ordering objects with Comparable and Comparator is taught in the Java Toolkit chapter.

04 · see it

Play with it in 3D

Press Space to play, ←/→ to step, drag to move, ? for all shortcuts. Try your own input.

sim

Design Linked List: Node objects and next references

The MyLinkedList object holds size and an arrow to a dummy node; each Node holds a value and a next arrow. An insert is two arrow changes, in the right order.

loading simulation…
sim

Design HashSet: hashCode picks the bucket, equality confirms

Each call searches one bucket only. With 5 buckets, keys 1, 6, 11 and 16 all land in bucket 1: a collision chain.

loading simulation…
sim

One class, many objects: static vs instance fields

Every ParkingLot object has its own free[] counts. The static opened counter has exactly one copy, stored with the class.

loading simulation…

05 · cost

Time & space, explained

Design choices and what they cost
Time per callMemorywhy
HashSet as an ArrayList of keysO(n)O(n)contains walks every stored key, and add/remove call it.
HashSet as boolean[10⁶ + 1]O(1)O(U), U = 10⁶ + 1One flag per possible key, used or not: the memory follows the key range, not the data.
HashSet with buckets + chainingO(1) average, O(n) worstO(n + B)A good hash spreads n keys over B buckets, so chains stay short; if every key collides, one chain holds all n.
Linked list: singly + dummy + sizeO(index)O(n)get, add and delete walk from the dummy to the position.
Linked list: doubly + two sentinelsO(min(index, n − index)); ends O(1)O(n)Walk from the nearer end; the sentinels make both ends instant.
Parking system with countersO(1)O(1)Only how many slots are free matters, not which ones.
Object basics and their cost
TimeNoteswhy
new Node(v)O(1)one heap object; fields start as 0, false or nullAllocation plus the constructor's few assignments.
Read a field / call a methodO(1)follows one referencenode.next.val is two reference hops.
Record equals / hashCode (k fields)O(k)generated from every componentEach component is compared or mixed once.
HashMap.get with a good hashCodeO(1) averagehash → bucket, then equals on a few entriesKeys are spread over many buckets.
HashMap.get when hashCode is constantO(n)every key in one bucketequals must run against keys one by one (Java 8+ tree bins help only for Comparable keys).
EnumType.values()O(k)returns a fresh copy of the constantsCache it in a static field if you call it in a loop.

06 · java

Raw vs Collections Framework

Constructors and encapsulation

javaconstructors, final and private fieldsjava.util.*
class Wallet {                        // a UPI-style wallet    private final String owner;              // final: set once, in the constructor    private long balancePaise;               // private: only the methods below can change it     Wallet(String owner) {        this(owner, 0);                      // reuse the other constructor    }     Wallet(String owner, long openingPaise) {        if (openingPaise < 0) throw new IllegalArgumentException("negative opening balance");        this.owner = owner;                  // this.owner is the field, owner is the parameter        this.balancePaise = openingPaise;    }     boolean pay(long paise) {                // every change goes through one checked door        if (paise <= 0 || paise > balancePaise) return false;        balancePaise -= paise;        return true;    }     long balance() {        return balancePaise;    }     String owner() {        return owner;    }}
  • this.owner = owner: this.owner is the field, owner is the parameter. Writing owner = owner assigns the parameter to itself and leaves the field empty.
  • this(owner, 0) calls another constructor of the same class. It must be the first statement.
  • final fields must be set exactly once, in the constructor; the compiler checks it.
  • private fields plus methods that validate = encapsulation. The wallet can never go negative, because pay is the only door.

Static vs instance fields

javaone shared counter, per-object countsjava.util.*
class ParkingLot {    static int opened = 0;                   // ONE copy, stored with the class    private final int[] free;                // one copy PER OBJECT: free[1] big, free[2] medium, free[3] small     ParkingLot(int big, int medium, int small) {        free = new int[] {0, big, medium, small};        opened++;                                    // bumps the shared count    }     boolean addCar(int carType) {        if (free[carType] == 0) return false;        free[carType]--;                             // changes only THIS lot        return true;    }}
  • A field without static exists once per object; a static field exists once per class. The sim shows each lot's own free[] and one shared opened.
  • Design problems: never keep per-object state in static fields. Judges create several objects, and all of them would share one counter.
  • A static field also survives between calls in the same run, so values from one test can leak into the next.

Static nested classes: static class Node

javaa stack built from static nested Nodesjava.util.*
class LinkedStack {    private static class Node {              // static nested: no hidden link to a LinkedStack        final int val;        final Node next;         Node(int val, Node next) {            this.val = val;            this.next = next;        }    }     private Node top;                        // null means empty    private int size;     void push(int x) {        top = new Node(x, top);              // the new node points at the old top        size++;    }     int pop() {        int val = top.val;        top = top.next;                      // the old top becomes garbage        size--;        return val;    }     int peek() {        return top.val;    }     boolean isEmpty() {        return top == null;    }     int size() {        return size;    }}
  • static class Node is an ordinary class that simply lives inside another one. No hidden reference, so new Node(5) works anywhere, even in static methods.
  • Mark it private when only the outer class uses it, as java.util.LinkedList does with its own Node.
  • This is the default for every node, entry or trie node you write inside a Solution or design class.

Inner classes: when the object needs its outer object

javaan iterator that reads the outer object's fieldjava.util.*
class Countdown implements Iterable<Integer> {    private final int start;     Countdown(int start) {        this.start = start;    }     public Iterator<Integer> iterator() {        return new Ticker();                 // each Ticker is tied to THIS Countdown    }     private class Ticker implements Iterator<Integer> {   // inner (no static): sees the outer object        private int next = start;            // reads the outer object's field directly         public boolean hasNext() {            return next >= 0;        }         public Integer next() {            return next--;        }    }}
  • An inner class (no static) stores a hidden reference to its outer object. That is one extra field per object, and an outer object must exist to create one.
  • Create an inner-class object from a static method and javac stops with "non-static variable this cannot be referenced from a static context".
  • Use an inner class only when the nested object really reads the outer object's fields. Iterators are the classic case: ArrayList's iterator is an inner class.

LeetCode's ListNode and TreeNode

javathe classes LeetCode gives youjava.util.*
// LeetCode already defines these above your Solution: use them, don't declare them again.class ListNode {    int val;                                 // package-private fields: read them directly    ListNode next;     ListNode() {}     ListNode(int val) {        this.val = val;    }     ListNode(int val, ListNode next) {        this.val = val;        this.next = next;    }} class TreeNode {    int val;    TreeNode left;    TreeNode right;     TreeNode() {}     TreeNode(int val) {        this.val = val;    }     TreeNode(int val, TreeNode left, TreeNode right) {        this.val = val;        this.left = left;        this.right = right;    }}
  • LeetCode shows these definitions in a comment above your Solution and compiles your code with the real classes. Use them; don't declare your own.
  • Fields are read and written directly: node.val, node.next, root.left. No getters.
  • null means "no node": the empty list and the empty tree are both null. Check for it first.
  • A dummy node (new ListNode()) in front of the head removes the special case for an empty result.

Interfaces: what vs how

javaone interface, two implementationsjava.util.*
interface IntStack {                         // WHAT a stack can do: no fields, no code    void push(int x);     int pop();     boolean isEmpty();} class ArrayIntStack implements IntStack {     // HOW, version 1: a growing int[]    private int[] items = new int[8];    private int size;     public void push(int x) {        if (size == items.length) items = Arrays.copyOf(items, size * 2);        items[size++] = x;    }     public int pop() {        return items[--size];    }     public boolean isEmpty() {        return size == 0;    }} class DequeIntStack implements IntStack {     // HOW, version 2: wrap an ArrayDeque    private final Deque<Integer> deque = new ArrayDeque<>();     public void push(int x) {        deque.push(x);    }     public int pop() {        return deque.pop();    }     public boolean isEmpty() {        return deque.isEmpty();    }} static int[] reversed(int[] values, IntStack stack) {   // works with ANY IntStack    for (int v : values) stack.push(v);    int[] out = new int[values.length];    for (int i = 0; i < out.length; i++) out[i] = stack.pop();    return out;}
  • An interface lists methods without bodies (plus optional default methods). A class that implements it must provide every one, as public methods.
  • reversed works with any IntStack: code that depends on the interface does not change when you swap the implementation.
  • The JDK is built this way: Deque → ArrayDeque or LinkedList, Map → HashMap or TreeMap. Declare by interface, construct by class.

equals and hashCode: the HashMap contract

javaa correct key vs a broken onejava.util.*
class Cell {                          // a grid position used as a HashSet key    final int r;    final int c;     Cell(int r, int c) {        this.r = r;        this.c = c;    }     @Override    public boolean equals(Object o) {        if (this == o) return true;                  // same object        if (!(o instanceof Cell other)) return false; // null or a different type        return r == other.r && c == other.c;         // same fields: equal    }     @Override    public int hashCode() {        return 31 * r + c;                           // equal cells get equal hash codes    }} class BrokenCell {                    // equals WITHOUT hashCode    final int r;    final int c;     BrokenCell(int r, int c) {        this.r = r;        this.c = c;    }     @Override    public boolean equals(Object o) {        return o instanceof BrokenCell other && r == other.r && c == other.c;    }    // no hashCode(): equal cells usually land in different buckets, so HashSet never compares them}
  • HashMap and HashSet call hashCode() to choose a bucket, then equals() inside it. Equal objects with different hash codes land in different buckets and are never compared.
  • The contract, from Object's documentation: equal objects must return equal hash codes. Unequal objects may share one; fewer collisions just means faster tables.
  • 31 * r + c (or Objects.hash(r, c), which boxes its arguments) mixes the fields. Use exactly the fields that equals uses.
  • Never change a key's fields while it sits in a HashSet: it is now in the wrong bucket and contains misses it.
  • o instanceof Cell other (Java 16+) checks the type and casts in one step.

Records for pairs, cells and keys

javarecord Point, record Edgejava.util.*
record Point(int r, int c) {}                // fields, constructor, r(), c(), equals, hashCode, toString record Edge(int to, int weight) {    Edge {                                   // compact constructor: validate, fields are set after it        if (weight < 0) throw new IllegalArgumentException("negative weight");    }     Edge heavier(int extra) {                // records are immutable: "change" means a new record        return new Edge(to, weight + extra);    }} static int distinctCells(int[][] moves) {    // Point as a HashSet key: no equals/hashCode to write    Set<Point> seen = new HashSet<>();    int r = 0, c = 0;    seen.add(new Point(r, c));    for (int[] m : moves) {        r += m[0];        c += m[1];        seen.add(new Point(r, c));    }    return seen.size();}
  • record Point(int r, int c) {} writes the private final fields, constructor, accessors r() and c(), equals, hashCode and toString for you (JEP 395, Java 16).
  • Records are immutable: to "change" one, build a new one. That is exactly what a HashMap key should be.
  • A compact constructor (Edge { … }) validates the arguments; the fields are assigned after it runs.
  • In hot loops over a grid you can skip objects entirely and encode a cell as one int: r * cols + c.

Generics: one implementation for every type

javaa bounded generic max, a generic Pairjava.util.*
static <T extends Comparable<T>> T max(List<T> items) {   // any T that can compare itself to a T    T best = items.get(0);    for (T item : items) {        if (item.compareTo(best) > 0) best = item;    }    return best;} class Pair<A, B> {                    // a tiny generic class (a record works too)    final A first;    final B second;     Pair(A first, B second) {        this.first = first;        this.second = second;    }}
  • <T> is a type parameter. The compiler checks the types, then erases them (type erasure), so your code needs no casts.
  • Bounded: <T extends Comparable<T>> means "any T that can compare itself to another T". That is what lets max call compareTo.
  • Only object types: List<Integer>, never List<int>. Primitives get boxed (see What Happens in Memory).
  • Erasure means no new T[n] and no instanceof List<Integer>. Use an ArrayList<T>, or an Object[] with a cast inside the class.
  • The JDK's Collections.max uses the more general bound <T extends Object & Comparable<? super T>>.

Enums for directions and states

javaDirection with data and methodsjava.util.*
enum Direction {    UP(-1, 0), RIGHT(0, 1), DOWN(1, 0), LEFT(0, -1);   // the only four Direction objects that will ever exist     private static final Direction[] ALL = values();    // values() copies the array on every call: cache it     final int dr;                                       // each constant carries its own data    final int dc;     Direction(int dr, int dc) {        this.dr = dr;        this.dc = dc;    }     Direction turnRight() {        return ALL[(ordinal() + 1) % 4];                // clockwise order: UP, RIGHT, DOWN, LEFT    }     Direction turnLeft() {        return ALL[(ordinal() + 3) % 4];    }} static int[] walk(String commands) {                    // F = forward, R = turn right, L = turn left    Direction d = Direction.UP;    int r = 0, c = 0;    for (char ch : commands.toCharArray()) {        switch (ch) {            case 'F' -> {                r += d.dr;                c += d.dc;            }            case 'R' -> d = d.turnRight();            case 'L' -> d = d.turnLeft();            default -> throw new IllegalArgumentException("unknown command " + ch);        }    }    return new int[] {r, c};}
  • An enum is a class with a fixed set of instances. Direction.UP is one object for the whole program, so == is safe.
  • Constants can carry data (dr, dc) and methods (turnRight), replacing parallel int[] dr = {…} arrays and magic numbers.
  • ordinal() is the declaration position. values() returns a new array on every call, so cache it in a static field.
  • States read better as enums: enum State { NEW, VISITING, DONE } for cycle detection beats 0/1/2. EnumMap is a map backed by an array.

Ordering objects

  • Comparable (one natural order, compareTo) and Comparator (any order you pass in) are taught in the Java Toolkit chapter.
  • Records and enums plug straight in: Comparator.comparingInt(Point::r).thenComparingInt(Point::c).
  • Keep compareTo consistent with equals: TreeSet and TreeMap treat compareTo(...) == 0 as a duplicate.
Java API you will actually use
calldoescost
new Node(v)Allocate one object; unset fields are 0, false or nullO(1)
this.field = param / this(args)Store a constructor argument / call another constructor (first line)O(1)
o instanceof Cell otherType check and cast in one step (Java 16+)O(1)
Objects.hash(a, b) / Objects.equals(a, b)Quick hashCode from fields (boxes them) / null-safe equalsO(k)
record P(int a, int b) {}Immutable data class with equals, hashCode, toStringO(1) per new
Integer.hashCode(x)Hash of an int: the value itselfO(1)
Math.floorMod(h, n)Bucket index that is never negativeO(1)
E.values() / e.ordinal() / E.valueOf(name)All constants (a new array) / position / constant by nameO(k) / O(1) / O(1)
new EnumMap<>(E.class)Array-backed map keyed by enum constantsO(1) per operation

A node class inside your solution

→ private static class Node

No hidden outer reference; can be created from static code.

A pair, tuple or grid cell as a HashMap key

→ A record: record Cell(int r, int c) {}

equals and hashCode are generated from both fields.

Your own class as a HashMap or HashSet key

→ Override equals AND hashCode from the same fields

The table needs both to find an equal key.

A fixed set of options (directions, states, types)

→ An enum

Type-safe, named, can carry data, and == is safe.

Two implementations behind one API

→ An interface + classes that implement it

Callers depend on what, not how.

Per-object state in a design problem

→ Instance fields (no static)

The judge creates several objects; static fields would be shared by all of them.

One algorithm for any comparable type

→ A generic method <T extends Comparable<T>>

Checked at compile time, no casts.

07 · recognise

Spot the pattern

"Design" or "Implement the X class" with a list of methods

Private fields for state, a constructor, one small checked method per operation

You need a Node, TrieNode or Entry inside your class

private static class Node

A pair or grid cell must be a HashMap key or go in a HashSet

A record, or equals + hashCode written together

Four directions, three states, a fixed set of types

An enum (or one constant int[][] table)

"Without using any built-in hash table / LinkedList library"

Build it from an array plus your own Node class

The method takes ListNode head or TreeNode root

LeetCode's classes: read val, next, left, right directly; null is empty

08 · traps & tricks

Mistakes to avoid, tricks to use

traps

  • Declaring class Node (no static) inside Solution and calling new Node() from a static method: compile error.
  • Using static fields for per-object state in a design problem: two objects share one counter.
  • Overriding equals without hashCode: HashSet and HashMap stop finding equal keys.
  • Writing equals(Cell other) instead of equals(Object o): that overloads instead of overriding, so collections never call it. @Override catches it.
  • Changing a key's fields after putting it in a HashMap or HashSet.
  • size = size; in a constructor: the parameter shadows the field. Write this.size = size;.
  • Redeclaring ListNode or TreeNode in a LeetCode answer.
  • List<int>: generics need object types. Use List<Integer>, or an int[].
  • Calling values() on an enum inside a hot loop: it allocates a new array every time.

tricks

  • In design rounds, write the fields first and say what each one means. The methods then almost write themselves.
  • Return exactly what the statement asks for on bad input (false, -1, do nothing). Judges test those calls.
  • Put @Override on equals, hashCode, toString and interface methods, so the compiler catches wrong signatures.
  • Reach for a record first for pairs and keys; write a full class only when fields must change.
  • Keep helper classes private static inside your Solution or design class, so everything pastes as one file.
  • Say the trade-off out loud: "I'm using a dummy node so the head needs no special case."
  • Simple hai: fields are the state, methods are the rules.

09 · why & how

Questions you might be too shy to ask

What exactly is this, and when must I write it?

this is the object the method is running on right now. You must write it when a parameter has the same name as a field: this.size = size means "my field = the parameter". You also use this(...) to call another constructor. Everywhere else, size alone already means the field, so this. is optional.

Static nested or inner class: does it really matter in an interview?

Yes, because of the compile error. Inside Solution, if Node is an inner class and you create one from a static helper, javac refuses with "non-static variable this cannot be referenced from a static context". Making it static fixes that and saves a hidden reference per node. Default to static.

Why does LeetCode's ListNode have no private fields and no getters?

It is a data holder for algorithms, and direct field access keeps solutions short. Encapsulation matters in design problems, where the class you write has rules to protect, like "free slots never go below zero".

Interface or abstract class?

For DSA and most design rounds, an interface. A class can implement many interfaces but extend only one class. Reach for an abstract class only when implementations share real code and fields.

Do I need hashCode if my class only goes into a TreeMap?

No: TreeMap and TreeSet use compareTo or a Comparator, never hashCode. But if the class might ever go into a HashMap or HashSet, override both equals and hashCode, or simply make it a record.

Can I use records in interviews and online assessments?

Records need Java 16 or newer. Check the Java version the platform shows; if it is older, use a small class with equals and hashCode, or encode the pair as one number (r * cols + c).

A design problem has no main. Who calls my methods, and why does the output say null?

The judge does. It runs new MyHashSet(), then calls your methods in the order the input lists them, and records what each one returns. The constructor and void methods return nothing, so their slot in the output shows null. Your only job: the object must give the right answer after any sequence of calls.

In Design HashSet the keys are never negative. Why use Math.floorMod instead of %?

For keys 0..10⁶, key % buckets gives the same answer. The habit matters when the hash can be negative (many String.hashCode() values are, because the int overflows): then % returns a negative index and the array access throws. Math.floorMod(h, n) always lands in 0..n − 1, so write it once and stop worrying.

Why 10,007 buckets? Does it have to be a prime?

Close to the number of keys (≤ 10⁴ here) keeps each chain about one node long. A prime count spreads patterned keys better: with 1,000 buckets, keys 0, 1000, 2000, … all crowd into bucket 0; with 10,007 they scatter. Prime is a helpful habit, not a rule: java.util.HashMap uses powers of two and mixes the hash bits instead.

Is a hash set O(1) or O(n)? What should I say in the interview?

Both are true, for different cases. O(1) average: a good hash spreads keys so each chain is short. O(n) worst: if every key lands in one bucket, a lookup walks all n. Say "O(1) average, O(n) worst case" for your own chained set — that one line shows you understand how it works. (java.util.HashMap goes one step further: since Java 8 a long chain becomes a red-black tree, so its worst case is O(log n) for Comparable keys like Integer.)

Is the dummy node part of the list? Does size count it?

No. The dummy is a helper that sits before the real head; its val is never read. size counts only real nodes, and the first real node is dummy.next. So for [1, 2], size is 2 and get(0) returns dummy.next.val, which is 1.

If I start at the dummy and step index times, why do I land before position index?

Think of the dummy as position −1. One step reaches position 0, two steps reach position 1, so index steps reach position index − 1: the node just before the target. That is exactly the node you need to insert or delete. Want the node at index? Take one more step.

Why may addAtIndex use index == size, but get and deleteAtIndex may not?

Count the choices for [1, 2, 3]. There are 3 nodes to read or delete: indexes 0..2, so index < size. But there are 4 gaps to insert into (before 1, before 2, before 3, after 3): indexes 0..3, so index <= size. Inserting at index == size is just adding at the tail.

Is OOP even tested in DSA rounds?

Indirectly, all the time: every linked-list and tree problem is objects and references, every hash-based solution relies on equals/hashCode, and "Design X" problems are graded on clean classes as much as on Big-O.

10 · practice

Interview problems

Every problem has brute → better → optimal solutions in Java, with complexity and a dry run.

  1. #705
    Design HashSet
    EasyAmazon
  2. #707
    Design Linked List
    MediumMicrosoftAmazon
  3. #1603

Also practise this here

Each problem has one home page in the course; these use this chapter's idea too. The note says what to look for.

  1. #622
    Design Circular Queue
    Mediumin Queues

    Another design class: an array plus two int fields (head, size), every method a guarded one-liner.

  2. #146
    LRU Cache
    Mediumin Linked Lists

    A private static class Node with prev/next plus a HashMap: Design Linked List and Design HashSet in one problem.

  3. #208
    Implement Trie (Prefix Tree)
    Mediumin Tries

    static class TrieNode { TrieNode[] next = new TrieNode[26]; boolean end; } is the same nested-class habit.

  4. #155
    Min Stack
    Mediumin Stacks

    State design: decide what each stack entry must remember (the value and the minimum so far).

  5. #49
    Group Anagrams
    Mediumin Arrays & Strings

    Why the HashMap key is a String and not a char[]: arrays use identity equals/hashCode, Strings compare contents.

11 · revise

The whole chapter on one screen

9 cards, about a minute. Read them the night before an interview, or right after the chapter to lock it in.

  1. what it is

    A class is the blueprint, an object one instance on the heap. Instance fields: one copy per object. static fields: one copy per class.

  2. template

    private static class Node { int val; Node next; Node(int v) { val = v; } } inside your class: no hidden outer reference.

  3. use it when

    "Design / Implement the X class": private fields for state, a constructor, one method per operation that checks, updates, returns.

  4. cost

    HashMap lookups are O(1) only if hashCode spreads the keys. A constant hashCode puts every key in one bucket: up to O(n) per lookup.

  5. trap

    Override equals without hashCode and HashSet/HashMap stop finding equal keys. Override both, from the same fields.

  6. trap

    static counters in a design class are shared by every object the judge creates. Per-object state = instance fields.

  7. pro tip

    Pairs, cells, composite keys: record Cell(int r, int c) {} gives equals, hashCode and toString for free (Java 16+).

  8. pro tip

    A fixed set of options (directions, states) → an enum. == is safe on enum constants; cache values().

  9. what it is

    Interfaces say what (Deque, Map, Comparable), classes say how. Declare by interface: Deque<Integer> st = new ArrayDeque<>();

12 · recall

Flip, remember, repeat

card 1 / 14
recall

Why private static class Node rather than private class Node inside Solution?

tap or Space to flip ↻

Question 1: Why private static class Node rather than private class Node inside Solution?

13 · check yourself

Quick quiz

  1. `class Lot { static int count; int free; }` Two Lot objects are created, and each runs `count++; free++;`. What does the first object see?

  2. Inside `class Solution`, `class Node {…}` (no static) is created with `new Node(1)` in a static method. Result?

  3. A class overrides equals (by fields) but not hashCode. After `set.add(new P(1, 2))`, what does `set.contains(new P(1, 2))` most likely return?

  4. Best way to use a (row, col) pair as a HashMap key in Java 17?

  5. Inserting node X after prev in a singly linked list. Which order is correct?

  6. Design Parking System with `int[] free` indexed by carType: what does addCar cost?

  7. Why cache `Direction.values()` in a static field?