Latin Squares and 2D Array Traversal, AP CS A – Study Notes
offline

Difficulty: Intermediate | Prerequisites: Comfortable with 1D arrays, basic loop traversal, method signatures in Java.

This topic covers Question 4 from the 2018 AP Computer Science A free-response section. It tests your ability to traverse 2D arrays, extract columns, and use helper methods to check properties of a grid. If you are comfortable with basic 1D array operations but have not yet worked with nested loops or 2D indexing, start there first. The question is a good bridge between simple array manipulation and the kind of multi-method problem-solving the AP exam rewards.


TL;DR

You write two methods in Java. getColumn pulls a single column out of a 2D array and returns it as a 1D array. isLatin checks whether a square grid is a Latin square, meaning every row and column contains exactly the same set of values with no duplicates. The trick is composing small helper methods (hasAllValues, containsDuplicates) rather than writing everything from scratch.


Key Terms

Latin square

A square grid of integers where the first row has no duplicates, and every row and every column contains exactly the same set of values as that first row. Think of it as a grid where each number appears exactly once per row and once per column.

2D array (two-dimensional array)

An array of arrays in Java, declared as int[][]. Each element is accessed with two indices: arr2D[row][col]. In simple terms, it is a grid with rows and columns, like a spreadsheet.

Column extraction

The process of reading down a single column of a 2D array and collecting those values into a new 1D array. In Java, columns are not stored contiguously, so you must loop through every row and pick out the element at the target column index.

Helper method

A method provided for you to call inside your own solution. On the AP exam, helper methods let you break a large problem into smaller, testable pieces. You do not implement them; you trust their specification and use them.

Precondition

A statement about what must be true before a method is called. If the precondition holds, the method guarantees correct behaviour. You do not need to check preconditions inside the method body.

Postcondition

A statement about what will be true after a method finishes executing. For example, "arr2D is unchanged" tells you the method does not modify the input array.

containsDuplicates(int[] arr)

Returns true if arr has any repeated values, false otherwise. Think of it as a quick "are all elements unique?" check.

hasAllValues(int[] arr1, int[] arr2)

Returns true if and only if every value in arr1 also appears somewhere in arr2. Both arrays must have the same length, and neither is modified. In simple terms, it checks whether arr2 contains everything that arr1 contains.


Core Content: Part (a), the getColumn Method

What it does

getColumn takes a 2D integer array and a column index c, and returns a new 1D array containing every element from column c, in the same top-to-bottom order they appear in the grid.

Signature

public static int[] getColumn(int[][] arr2D, int c)

Implementation strategy

  • Create a new int[] whose length equals the number of rows in arr2D. The number of rows is arr2D.length.

  • Loop through each row index r from 0 to arr2D.length - 1.

  • At each row, copy arr2D[r][c] into position r of the new array.

  • Return the new array.

Sample solution

public static int[] getColumn(int[][] arr2D, int c) {
    int[] result = new int[arr2D.length];
    for (int r = 0; r < arr2D.length; r++) {
        result[r] = arr2D[r][c];
    }
    return result;
}

Worked example

Given:

arr2D = { {0, 1, 2},
          {3, 4, 5},
          {6, 7, 8},
          {9, 5, 3} }

Calling getColumn(arr2D, 1) loops through rows 0 to 3, pulling arr2D[0][1] = 1, arr2D[1][1] = 4, arr2D[2][1] = 7, arr2D[3][1] = 5.

Result: {1, 4, 7, 5}.

Core Content: Part (b), the isLatin Method

What is a Latin square?

A 2D square array of integers is a Latin square when all three of these conditions hold:

  1. The first row has no duplicate values.

  1. Every row in the square contains exactly the same values as the first row.

  1. Every column in the square contains exactly the same values as the first row.

Conditions 2 and 3 together mean that every row and every column is a permutation of the same set of values.

Signature

public static boolean isLatin(int[][] square)

Implementation strategy

You are told to use the helper methods getColumn, hasAllValues, and containsDuplicates. The approach breaks down neatly:

  • Check condition 1: Call containsDuplicates(square[0]). If it returns true, the first row has repeats, so return false.

  • Check condition 2 (all rows): Loop through each row from index 1 onward. For each row, call hasAllValues(square[0], square[r]) to confirm the first row's values all appear in this row. If any call returns false, return false.

  • Check condition 3 (all columns): Loop through each column index from 0 to square[0].length - 1. For each column, call getColumn(square, c) to extract the column as a 1D array, then call hasAllValues(square[0], col) to confirm the first row's values all appear in that column. If any call returns false, return false.

  • If all checks pass, return true.

A subtle point: hasAllValues alone is not enough to rule out duplicates within a row or column. But because hasAllValues requires that every value in arr1 appears in arr2, and both arrays have the same length (guaranteed by the precondition that the grid is square), any missing value would force a duplicate. So hasAllValues combined with equal lengths effectively checks both "same values" and "no duplicates" for rows and columns, as long as the first row itself has no duplicates (condition 1).

Sample solution

public static boolean isLatin(int[][] square) {
    if (containsDuplicates(square[0])) {
        return false;
    }
    for (int r = 1; r < square.length; r++) {
        if (!hasAllValues(square[0], square[r])) {
            return false;
        }
    }
    for (int c = 0; c < square[0].length; c++) {
        int[] col = getColumn(square, c);
        if (!hasAllValues(square[0], col)) {
            return false;
        }
    }
    return true;
}

Why this works

The first row is the reference set. If it has no duplicates and every other row and column contains all the same values (checked via hasAllValues), then no row or column can have duplicates either, because there is no room for a repeat in an array of the same length that must contain every value from the duplicate-free reference.


Examples and Non-Examples

Valid Latin squares

3x3 example:

Col 0

Col 1

Col 2

Row 0

1

2

3

Row 1

2

3

1

Row 2

3

1

2

Every row and column contains {1, 2, 3} with no repeats.

4x4 example:

Col 0

Col 1

Col 2

Col 3

Row 0

10

30

20

0

Row 1

0

20

30

10

Row 2

30

0

10

20

Row 3

20

10

0

30

Every row and column contains {0, 10, 20, 30} with no repeats.

Not Latin squares

Duplicate in first row:

1

2

1

2

1

1

1

1

2

Fails condition 1: the first row has two 1s.

Rows do not share the same values:

1

2

3

3

1

2

7

8

9

Fails condition 2: the third row {7, 8, 9} does not contain the first row's values.

Columns do not share the same values:

1

2

1

2

Fails condition 3: column 0 is {1, 1}, which does not contain the value 2 from the first row. Also has duplicates in both columns.


Common Misconceptions

  • Students often confuse arr2D.length (number of rows) with arr2D[0].length (number of columns). In getColumn, the result array's length is the number of rows, not columns.

  • Some students try to access a column directly as arr2D[c]. This gives you row c, not column c. Java 2D arrays are row-major: the first index is always the row.

  • Students sometimes forget that hasAllValues only checks one direction: whether every value in arr1 appears in arr2. It does not check whether arr2 has extra values. In a square grid of equal-length arrays, this is fine, because there is no room for extras. But understanding the one-directional nature of the check matters.

  • A common mistake in isLatin is skipping the containsDuplicates check on the first row. If the first row has duplicates, hasAllValues can still return true for other rows (they might contain the same multiset), and you would incorrectly classify the grid as a Latin square.


Why It Matters / Exam Flags

  • ⚠️ Part (a) is a classic "column extraction" pattern. The AP exam frequently tests 2D array traversal where you loop over rows to collect a column. Get this pattern cold.

  • ⚠️ Part (b) tests your ability to compose helper methods. The AP rubric awards points specifically for calling getColumn, hasAllValues, and containsDuplicates correctly. Writing your own duplicate-check logic instead of calling the provided method will lose marks.

  • ⚠️ Make sure you start your row loop at index 1 (not 0) when comparing other rows to the first row. Comparing the first row to itself wastes a call but will not cause a wrong answer, so it is not penalised, but it shows you are not thinking clearly about the problem.

  • ⚠️ The column loop should run from 0 to square[0].length - 1. Every column must be checked, including column 0.


Quick Self-Test

  1. True or false: arr2D.length gives you the number of columns in a 2D array. (False. It gives the number of rows.)

  1. True or false: hasAllValues(a, b) checks whether b contains every element of a. (True.)

  1. Fill in the blank: In getColumn, the returned array has length equal to ________. (arr2D.length, i.e. the number of rows.)

  1. True or false: A Latin square can have duplicate values in its first row. (False. The first row must be duplicate-free.)

  1. True or false: If you forget to call containsDuplicates on the first row, isLatin could return true for a grid that is not a Latin square. (True.)


Practice Q&A

Q: Write the body of getColumn for a 2D array arr2D and column index c.

A: Create an int[] of length arr2D.length. Loop r from 0 to arr2D.length - 1, setting result[r] = arr2D[r][c]. Return result.

Q: In isLatin, why do you check containsDuplicates(square[0]) before anything else?

A: Because the definition requires the first row to have no duplicates. If it does, the grid cannot be a Latin square regardless of what the other rows and columns look like. It also ensures that hasAllValues checks on subsequent rows and columns are meaningful.

Q: Suppose square = {{2, 2}, {2, 2}}. Walk through isLatin and explain the result.

A: containsDuplicates(square[0]) receives {2, 2}, which contains a duplicate, so it returns true. The method immediately returns false. The grid is not a Latin square.

Q: Could you use containsDuplicates on every row and column instead of hasAllValues? Why or why not?

A: Checking for duplicates alone is not sufficient. A row could have all unique values that differ entirely from the first row's values (e.g., first row {1, 2, 3} and another row {4, 5, 6}). hasAllValues catches this; containsDuplicates would not.

Q: What does getColumn(square, 0) return for square = {{10, 30, 20, 0}, {0, 20, 30, 10}, {30, 0, 10, 20}, {20, 10, 0, 30}}?

A: {10, 0, 30, 20}.


Connections to Other Topics

This connects to 2D array traversal more broadly: any question that asks you to process rows, columns, diagonals, or subregions of a grid uses the same row-major indexing pattern. The idea of composing helper methods is central to the AP CS A exam's emphasis on abstraction and code reuse. You will see similar "call these provided methods" patterns in other FRQs. Latin squares also appear in combinatorics and experimental design (randomised block designs), so the concept crosses into statistics and discrete mathematics.


Related Terms / Search Tags

Latin square, AP CS A 2018 FRQ 4, ArrayTester, getColumn, isLatin, hasAllValues, containsDuplicates, 2D array traversal, column extraction, nested loop, row-major order, helper method composition, precondition, postcondition, AP Computer Science A free response, square matrix, permutation matrix, array of arrays, int[][] Java