Skip to the notes
JSGroundwork
JSGroundwork handwritten · web dev
✎Playground→⌘Problems↻Review🔥Progress

Chapters

34 chapters
⌕
Beginner10›
B1Complexity analysisB2Arrays & stringsB3HashingB4Two pointersB5Sliding windowB6Binary searchB7Sorting algorithmsB8Stacks & queuesB9Linked listsB10Basic recursion
Intermediate12›
I1TreesI2Tree problems in depthI3Heaps & priority queuesI4Graphs: representationI5Graph problemsI6BacktrackingI7DP: 1DI8DP: 2DI9Greedy algorithmsI10IntervalsI11Bit manipulationI12Matrix problems
Advanced12›
A1Advanced DPA2Union-FindA3Advanced graph algorithmsA4Minimum Spanning TreeA5TriesA6Segment & Fenwick treesA7String algorithmsA8Monotonic stack & queueA9Design problemsA10Advanced backtrackingA11Topological patternsA12Interview strategy
/ search[ ] chaptert top

DSA in JS levels

1Beginner2Intermediate3Advanced

Ready to read

JSJavaScript⑂Git◎Interview prepΣDSA in JSSDSystem Design
More topics15›
</>HTML{ }CSS⚛ReactNNext.jsNeNest.jsTSTypeScriptNoNode.js🐳DockerDBSQL & Databases✓Testing🔒Web Security☁Cloud & DevOps◈GraphQL◆Redis☸Kubernetes
100%
I12

Matrix problems

A grid is an array of arrays — every trick here is index bookkeeping, done carefully.

Traversal direction — the pattern behind spiral order

1 2 3 4 10 11 12 5 9 8 7 6 right along the top → down the right side → left along the bottom → up the left side → shrink boundary, repeat
Four boundaries (top/right/bottom/left) that shrink inward after each full loop.
function spiralOrder(matrix) {
  const result = [];
  let top = 0, bottom = matrix.length - 1;
  let left = 0, right = matrix[0].length - 1;

  while (top <= bottom && left <= right) {
    for (let c = left; c <= right; c++) result.push(matrix[top][c]);
    top++;
    for (let r = top; r <= bottom; r++) result.push(matrix[r][right]);
    right--;
    if (top <= bottom) { // guard: this row may already be consumed
      for (let c = right; c >= left; c--) result.push(matrix[bottom][c]);
      bottom--;
    }
    if (left <= right) { // guard: this column may already be consumed
      for (let r = bottom; r >= top; r--) result.push(matrix[r][left]);
      left++;
    }
  }
  return result;
}
⚠ The two guards aren't optional On a non-square matrix (e.g. a single row, or a single column), skipping the if (top <= bottom) / if (left <= right) checks re-visits cells that the earlier two loops already covered — this is the single most common bug in spiral-order implementations.

In-place rotation — 90° with no extra matrix

Rotating 90° clockwise decomposes into two simpler, well-known operations: transpose (flip across the diagonal), then reverse each row.

123 456 789 transpose → 147 258 369 reverse rows → 741 852 963
Two well-understood O(n²) passes compose into a correct 90° clockwise rotation, in place.
function rotate(matrix) {
  const n = matrix.length;

  // transpose: swap matrix[r][c] with matrix[c][r]
  for (let r = 0; r < n; r++) {
    for (let c = r + 1; c < n; c++) { // c starts at r+1 — never touch the diagonal or repeat a swap
      [matrix[r][c], matrix[c][r]] = [matrix[c][r], matrix[r][c]];
    }
  }

  // reverse each row
  for (const row of matrix) row.reverse();
}

Grid as a graph — search patterns from the graph chapter, reused

Any grid problem involving "connected region," "flood fill," or "shortest path between cells" is the graph-traversal chapters applied directly: each cell is a node, each of its up-to-4 orthogonal neighbors is an edge.

function numIslands(grid) {
  const rows = grid.length, cols = grid[0].length;
  let islands = 0;

  function sink(r, c) {
    if (r < 0 || r >= rows || c < 0 || c >= cols || grid[r][c] !== "1") return;
    grid[r][c] = "0"; // mark visited by mutating the grid — avoids a separate visited set
    sink(r + 1, c); sink(r - 1, c); sink(r, c + 1); sink(r, c - 1);
  }

  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      if (grid[r][c] === "1") {
        islands++;
        sink(r, c); // flood-fill the whole island so it's never counted twice
      }
    }
  }
  return islands;
}
Say it like this → "I'll treat each cell as a graph node with up to four neighbors and reuse a flood-fill DFS — this is the exact same connected-components idea from the graph chapter, just with grid coordinates standing in for an adjacency list."

Recognizing it in an unseen problem

  • "Spiral," "rotate," "transpose," "diagonal" → boundary/index bookkeeping, work out the pattern on paper first
  • "Islands," "regions," "flood fill," "shortest path in a grid" → it's a graph problem wearing a grid costume
  • In-place mutation requested → look for a decomposition into two or more simpler, already-known transformations (like rotate = transpose + reverse)
  • Always double-check boundary conditions on non-square grids — single row/column inputs break naive boundary logic first
Practice this layer

Opens in the editor — write it, run it, and check it against real tests.

Spiral Matrix5 tests · beginnerRotate Image5 tests · intermediateSet Matrix Zeroes5 tests · advancedSearch a 2D Matrix II5 tests · advanced
←previousBit manipulation↑ CovernextAdvanced DP→