CompSci.rocks
FRQcsapa

ArrayResizer: 2021 FRQ 4

A step-by-step solution to the 2021 AP CSA FRQ 4 (ArrayResizer), covering 2D array traversal and building a smaller 2D array from selected rows in Java.

Trimming down a two-dimensional array to just its "clean" rows is the challenge in this AP Computer Science A free-response question — first testing a single row for zeros, then using that test (plus a provided helper) to build a smaller replacement array.

What This FRQ Tests

  • AP CSA units: Unit 8 (2D Array) and Unit 4 (Iteration)
  • Core skill: looping across one row of a 2D array to check every value in it
  • Secondary skill: allocating a brand-new 2D array at the correct size up front, since Java arrays can't be resized after creation
  • Official category: "2D Array" — always FRQ 4 on the AP CSA exam

The Setup

  • ArrayResizer has three static methods (this problem doesn't use instance fields at all — everything operates on an int[][] passed in as a parameter):
    • boolean isNonZeroRow(int[][] array2D, int r) — for you to write in part (a)
    • int numNonZeroRows(int[][] array2D) — already written for you; counts how many rows contain no zeros at all
    • int[][] resize(int[][] array2D) — for you to write in part (b), using the other two methods
  • Despite the "Array/ArrayList"-sounding name, this problem is squarely a 2D array question — every method takes an int[][] parameter, not a 1D array or an ArrayList.

Part (a): Writing isNonZeroRow(int[][] array2D, int r)

The Rule, Broken Down

  1. Look at every value in row r only — not the whole 2D array.
  2. If every value in that row is non-zero, return true.
  3. If any value in that row is zero, return false.

Step-by-Step Approach

  1. Loop across every column index of row r.
  2. At each column, check whether that value is 0.
  3. The moment a zero is found, the row already fails the test — return false immediately, no need to keep checking.
  4. If the loop finishes without ever finding a zero, the row passed — return true.

The Code

public static boolean isNonZeroRow(int[][] array2D, int r)
{
    for (int c = 0; c < array2D[r].length; c++)
    {
        if (array2D[r][c] == 0)
        {
            return false;
        }
    }

    return true;
}

Why Each Piece Matters

  • array2D[r].length, not array2D.lengtharray2D[r] is itself a 1D array (row r), and its .length gives the number of columns in that specific row. array2D.length would instead give the total number of rows in the whole 2D array, which is the wrong number to loop against here.
  • Returning false immediately inside the loop — as soon as one zero is found, there's no need to look at the rest of the row; the answer is already decided. This also naturally avoids ever needing a separate boolean flag variable.
  • Returning true only after the loop finishes — this is what correctly handles the case where the loop finds no zeros at all, including the edge case of checking every single column without a single early return firing.

Tracing the Example

Using the array from the question:

int[][] arr = { {2, 1, 0},
                {1, 3, 2},
                {0, 0, 0},
                {4, 5, 6} };
Call Row checked Values Result
isNonZeroRow(arr, 0) row 0 2, 1, 0 false — hits a 0 at column 2
isNonZeroRow(arr, 1) row 1 1, 3, 2 true — no zero anywhere
isNonZeroRow(arr, 2) row 2 0, 0, 0 false — hits a 0 at column 0
isNonZeroRow(arr, 3) row 3 4, 5, 6 true — no zero anywhere

All four results match the table given in the question.

Common Mistakes to Avoid

  • Looping with array2D.length instead of array2D[r].length. This is the classic 2D array mix-up — one gives the row count, the other gives a specific row's column count, and using the wrong one either misses columns or throws an ArrayIndexOutOfBoundsException.
  • Continuing to loop after finding a zero, using a boolean flag instead of returning early. This works too, but it's easy to get the flag logic backwards; returning false the instant a zero shows up is simpler and harder to mess up.
  • Checking array2D[r][c] != 0 and trying to build the logic around that instead of the zero case. Either direction can work, but starting from "is this the failing condition?" (a zero) keeps the early-return pattern clean.

Part (b): Writing resize(int[][] array2D)

The Rule, Broken Down

  1. Build a new 2D array containing only the rows from array2D that have no zeros.
  2. Keep those rows in the same relative order they appeared in originally.
  3. The original array2D must be left unchanged.
  4. You're required to actually use both numNonZeroRows and isNonZeroRow to receive full credit — not reimplement their logic yourself.

Step-by-Step Approach

  1. Java arrays can't grow or shrink after creation, so figure out the new array's size before creating it. That's exactly what numNonZeroRows(array2D) is for — call it once to get the row count for the new array.
  2. Create the new 2D array with that many rows, and the same number of columns as the original (array2D[0].length).
  3. Keep a separate counter for which row of the new array to fill in next, starting at 0.
  4. Loop over every row index of the original array2D.
  5. For each row, call isNonZeroRow to check if it qualifies. If it does, copy every value in that row into the next open row of the new array, then advance the new-array row counter.
  6. After the loop, return the new array.

The Code

public static int[][] resize(int[][] array2D)
{
    int[][] result = new int[numNonZeroRows(array2D)][array2D[0].length];
    int newRow = 0;

    for (int r = 0; r < array2D.length; r++)
    {
        if (isNonZeroRow(array2D, r))
        {
            for (int c = 0; c < array2D[r].length; c++)
            {
                result[newRow][c] = array2D[r][c];
            }

            newRow++;
        }
    }

    return result;
}

Why Each Piece Matters

  • numNonZeroRows(array2D) used for the new array's row count — this is precisely why that helper method was provided. Without knowing the count in advance, there'd be no way to size a plain Java array correctly (unlike an ArrayList, which can just grow as you go).
  • isNonZeroRow(array2D, r) used to decide whether to copy row r — reusing part (a)'s method here means the logic for "what counts as a good row" only lives in one place, matching the rubric's requirement to actually use it rather than duplicate its logic inline.
  • A separate newRow counter, distinct from rr walks every row of the original array (including skipped ones), while newRow only advances when a row is actually copied. These two numbers fall out of sync as soon as the first zero-containing row is skipped, which is exactly why they need to be tracked separately.
  • The inner loop copies one row at a time — since rows are themselves arrays, an entire row can't be assigned in one step (result[newRow] = array2D[r]; would copy a reference to the original row's array rather than its values, which technically works for reading here but is fragile style; looping value-by-value is the safer, more explicit habit).

Tracing the Example

Using the same array as part (a):

int[][] arr = { {2, 1, 0},
                {1, 3, 2},
                {0, 0, 0},
                {4, 5, 6} };
  • numNonZeroRows(arr)2 (only rows 1 and 3 qualify), so result = new int[2][3].
  • r = 0: isNonZeroRow(arr, 0) is false — skipped, newRow stays 0.
  • r = 1: isNonZeroRow(arr, 1) is true — copy {1, 3, 2} into result[0], then newRow becomes 1.
  • r = 2: isNonZeroRow(arr, 2) is false — skipped, newRow stays 1.
  • r = 3: isNonZeroRow(arr, 3) is true — copy {4, 5, 6} into result[1], then newRow becomes 2.

Final result: { {1, 3, 2}, {4, 5, 6} } — matches the question's expected contents of smaller exactly.

Common Mistakes to Avoid

  • Not using numNonZeroRows at all (e.g., guessing a size, or building an ArrayList and converting it) — the problem explicitly requires using it for full credit, and it's also the only clean way to know the new array's size before creating it.
  • Sizing the new array with array2D.length instead of numNonZeroRows(array2D). That would create a new array the same size as the original, defeating the entire point of "resizing" it, and leaving trailing rows filled with zeros.
  • Advancing newRow on every iteration of the outer loop, instead of only when a row is actually copied. This leaves gaps (all-zero rows) in the new array instead of a tightly packed result.
  • Using array2D.length for the number of columns instead of array2D[0].length. Rows and columns are easy to swap by accident — always double check which dimension a given .length is measuring.

Key Takeaways

  • array2D.length gives the number of rows; array2D[someRow].length gives the number of columns in that row — mixing these up is the most common 2D array bug on the AP exam.
  • Plain Java arrays are fixed-size the moment they're created, so "build a new array containing only some elements" always starts with figuring out the final size first — often via a helper method written just for that purpose.
  • When copying selected rows into a smaller array, track two separate indices: one walking the original structure, one walking the new one — they only stay in sync until the first item gets skipped.

Related FRQs