Arrays & Strings
"The foundation of all data structures. Master these, and you master 80% of interview patterns."
Arrays and Strings are the most common interview topic at every FAANG company — by a significant margin. Virtually every other data structure (stack, queue, hash map, graph adjacency list) is implemented on top of an array internally. Mastering array patterns (prefix sums, two pointers, sliding window, Dutch National Flag) and string patterns (frequency arrays, rolling hash, StringBuilder) is non-negotiable. If you solve only one lecture deeply, make it this one.
📖 What is an Array?
An array is a fixed-size, contiguous block of memory
that holds elements of the same type. When you write
int[] arr = new int[5] in Java, the JVM reserves 5×4 = 20
bytes of contiguous heap memory, all zeroed out. The address of
arr[0] is some base address B, and arr[i] is
at B + i×4. This is why
random access is O(1) — you don't search; you
calculate.
📐 What are 2D Arrays?
A 2D array is an array of arrays — a table with rows
and columns. In Java,
int[][] matrix = new int[3][4] creates 3 separate 1D
arrays, each of size 4. The rows are NOT necessarily contiguous in
memory (unlike C's 2D arrays), because each row is an independent
object on the heap.
• Row/Column traversal: nested for loops
• Diagonal traversal: elements where
i == j (main) or
i + j == n-1 (anti-diagonal)• Transpose: swap
grid[i][j] ↔
grid[j][i]• Spiral order: shrinking boundary: top/bottom/left/right
• 2D Prefix Sum: O(1) submatrix sum queries (covered in this lecture)
🧵 What is a String in Java? — The Deep Dive
In Java, a String is an
immutable sequence of char values.
Internally, it is backed by a char[] (before Java 9) or a
byte[] with a coder flag (Java 9+ compact strings).
"Immutable" means once a String object is created, it
cannot be changed — ever. Any operation that appears to "modify" a
string actually creates a brand new String object.
StringBuilder is a mutable buffer. It holds a
resizable char[] array internally and appends directly
— no new objects. Use StringBuilder inside any loop
that builds a string.Complexity comparison:
String += char in a loop → O(N²) timeStringBuilder.append(char) in a loop → O(N) time, O(1)
amortized per append
// String — immutable, stored in String Pool String s1 = "hello"; // String Pool object String s2 = "hello"; // SAME pool object! s1 == s2 is TRUE String s3 = new String("hello"); // NEW heap object. s1 == s3 is FALSE // Always use .equals() not == to compare String content! s1.equals(s2) // → true ✓ s1.equals(s3) // → true ✓ s1 == s3 // → false ← TRAP! different heap objects // Key String methods (all return NEW String objects): s.length() // O(1) — stored as field s.charAt(i) // O(1) — array lookup s.substring(l, r) // O(r-l) — copies chars s.indexOf("sub") // O(N*M) naive search s.toCharArray() // O(N) — creates new char[] s.toLowerCase() // O(N) — new String s.trim() // O(N) — new String String.valueOf(42) // Integer → String Integer.parseInt("42") // String → int // StringBuilder — mutable, heap-allocated buffer StringBuilder sb = new StringBuilder(); sb.append("hello"); // O(1) amortised sb.append(' '); sb.append("world"); sb.insert(5, ","); // O(N) — shifts chars sb.delete(5, 6); // O(N) — shifts chars sb.reverse(); // O(N) — in-place reverse sb.toString(); // O(N) — creates final String
| Operation | String | StringBuilder |
|---|---|---|
| Access char at index | O(1) charAt(i) |
O(1) charAt(i) |
| Append single char | O(N) — creates new String | O(1) amortised |
| Append N chars in loop | O(N²) total | O(N) total |
| Reverse | O(N) new String | O(N) in-place |
| Thread safety | ✅ Immutable = always safe | ❌ Not thread-safe (use StringBuffer) |
| Memory | String Pool deduplication | Always new heap object |
String is
a heap object. Lecture 7 (Math) uses numeric string parsing (n % 10
to extract digits). The Hashing lecture (part of Phase 2) builds
directly on frequency arrays introduced here.
📊 Prefix Sums (1D and 2D)
Imagine your bank account. If you want to know exactly how much you spent between Wednesday and Friday, you don't need to manually add up Wednesday + Thursday + Friday's receipts. You can simply look at your total balance on Friday and subtract your total balance from Tuesday!
This is the essence of Prefix Sums. They allow you to answer multiple Range Sum Queries in O(1) time after a single O(N) precomputation step.
1.1 The 1D Memory Logic
We build a new array P where P[i] stores the
sum of all elements from index 0 up to i.
// Step 1: Precompute (Build Prefix Array) P[0] = A[0] for (int i = 1; i < N; i++) { P[i] = P[i-1] + A[i]; } // Step 2: Query(L, R) in O(1) return P[R] - (L > 0 ? P[L-1] : 0);
1.2 The 2D Submatrix Logic
What if you have a 2D matrix (like a grid of numbers) and you want the sum of a specific rectangle? Treating the matrix as a series of rectangles. Doing nested loops takes an incredibly slow O(N²M²). Using the Inclusion-Exclusion Principle, you can find the sum of any sub-rectangle in O(1).
| Operation | Formula Statement |
|---|---|
| Precompute `P[i][j]` |
arr[i][j] + P[i-1][j] + P[i][j-1] - P[i-1][j-1]
Add the cell itself + rectangle ABOVE + rectangle LEFT. Subtract corner added twice. |
| Query `(r1,c1)` to `(r2,c2)` |
P[r2][c2] - P[r1-1][c2] - P[r2][c1-1] + P[r1-1][c1-1]
Take giant rectangle. Cut top. Cut left. Add top-left corner back once. |
// To find sum from (r1, c1) to (r2, c2): Sum = P[r2][c2] - P[r1-1][c2] - P[r2][c1-1] + P[r1-1][c1-1];
🎒 Kadane's Algorithm — Maximum Subarray
If you're looking for the largest possible sum from a contiguous chunk of an array, checking every chunk takes O(n³). Kadane's algorithm finds the answer in a blazingly fast O(N) single pass!
2.1 The "Reset" Philosophy
Kadane’s works on a simple intuition: If my past is dragging me down (negative sum), I must drop the baggage and start fresh!
At every step, you must make a greedy choice: Do I append the current number to the existing subarray chain, OR do I cut ties and start a brand-new subarray at this exact number?
public int maxSubArray(int[] nums) { int currentMax = nums[0]; int globalMax = nums[0]; for (int i = 1; i < nums.length; i++) { // Core Philosophy: Accept the baggage or ditch it? currentMax = Math.max(nums[i], currentMax + nums[i]); globalMax = Math.max(globalMax, currentMax); } return globalMax; }
minProduct and maxProduct) or the
Best Time to Buy & Sell Stock (track the
minPrice).
🚩 Dutch National Flag (Three-Way Partition)
Invented by Edsger Dijkstra, the DNF approach sorts 0s, 1s, and 2s in
a single pass using O(1) space. It maintains 4
strictly defined zones using 3 pointers: low,
mid, and high.
-
0 to low - 1: Confirmed 0s zone. -
low to mid - 1: Confirmed 1s zone. mid to high: UNEXPLORED zone.-
high + 1 to N - 1: Confirmed 2s zone.
public void sortColors(int[] nums) { int low = 0, mid = 0, high = nums.length - 1; while (mid <= high) { if (nums[mid] == 0) { swap(nums, low++, mid++); } else if (nums[mid] == 1) { mid++; } else { swap(nums, mid, high--); } } }
🔄 The Reversal Algorithm (In-Place)
Rotating an array by K steps in O(1) space using three
strategic mirror flips.
[1, 2, 3, 4, 5] by K=2:
- Reverse whole:
[5, 4, 3, 2, 1] - Reverse first K:
[4, 5, 3, 2, 1] - Reverse rest:
[4, 5, 1, 2, 3]
public void rotate(int[] nums, int k) { int n = nums.length; k %= n; reverse(nums, 0, n - 1); reverse(nums, 0, k - 1); reverse(nums, k, n - 1); }
🔤 Frequency Arrays vs HashMaps
HashMaps can be slow for constant character sets like lowercase
English letters. A flat int[26] array is the absolute
fastest way to count frequencies.
int[] count = new int[26]; for (char c : s.toCharArray()) { count[c - 'a']++; }
🧵 String Immutability
Strings in many languages are immutable. Operations like
s += "abc" create brand new objects, leading to O(N²)
time when done in loops.
+= on a String
inside a loop. Always use StringBuilder for
high-performance mutations.
StringBuilder sb = new StringBuilder(); for (char c : chars) sb.append(c); return sb.toString();
🔑 Rabin-Karp & Rolling Hash
Rabin-Karp enables O(N) substring searching by converting windows to numerical hashes. The Rolling Hash allows O(1) hash updates as the window slides.
\[ H_{new} = (H_{old} - S[i-1] \cdot P^{M-1}) \cdot P + S[i+M-1] \]
🌀 2D Boundary Traversals
Navigating matrices in orders like Spiral or Zigzag is easiest when
using 4 dynamic, shrinking walls: top,
bottom, left, and right.
while (top <= bottom && left <= right) { // Paint Top wall, increment top // Paint Right wall, decrement right // Paint Bottom wall, decrement bottom // Paint Left wall, increment left }
💪 Practice Problems
Master these core patterns through hands-on practice.
📝 Assignment
Complete the comprehensive practice set covering Prefix Sums, Kadane's, Matrix manipulations, and String logic.
Open Assignment.md →
✅ Topic 8 Completion Checklist
Master the physics of data organization. From Merge Sort's Divide & Conquer to QuickSort's Partitioning — foundations for advanced recursion.