Question 1: 2D Arrays

“Students will be asked to write program code to satisfy method specifications using expressions, conditional statements, and iterative statements and create, traverse, and manipulate elements in 2D array objects.”

While this problem does focus a lot on iterating through and traversing 1D arrays, ultimately, these methods and focuses shift to a larger focus on 2D arrays. It has some elements of an Arrays/ArrayList problem, but ultimately focuses more on 2D arrays by the end.

Question Description

This question involves reasoning about one-dimensional and two-dimensional arrays of integers. You will write three static methods, all of which are in a single enclosing class, named DiverseArray (not shown). The first method returns the sum of the values of a one-dimensional array; the second method returns an array that represents the sums of the rows of a two-dimensional array; and the third method analyzes row sums.

a. Write a static method arraySum that calculates and returns the sum of the entries in a specified one-dimensional array. The following example shows an array arr1 and the value returned by a call to arraySum.

b. Write a static method rowSums that calculates the sums of each of the rows in a given two-dimensional array and returns these sums in a one-dimensional array. The method has one parameter, a two-dimensional array arr2D of int values. The array is in row-major order: arr2D [r] [c] is the entry at row r and column c. The method returns a one-dimensional array with one entry for each row of arr2D such that each entry is the sum of the corresponding row in arr2D. As a reminder, each row of a two-dimensional array is a one-dimensional array. Assume that arraySum works as specified, regardless of what you wrote in part (a). You must use arraySum appropriately to receive full credit.

c. A two-dimensional array is diverse if no two of its rows have entries that sum to the same value. In the following examples, the array mat1 is diverse because each row sum is different, but the array mat2 is not diverse because the first and last rows have the same sum. Write a static method isDiverse that determines whether or not a given two-dimensional array is diverse. The method has one parameter: a two-dimensional array arr2D of int values. The method should return true if all the row sums in the given array are unique; otherwise, it should return false. In the arrays shown above, the call isDiverse (mat1) returns true and the call isDiverse(mat2) returns false. Assume that arraySum and rowSums work as specified, regardless of what you wrote in parts (a) and(b). You must use rowSums appropriately to receive full credit.

Code

All methods referred to in the three parts are shown in the object below. A code comment above clarifies where it comes from.

public class DiverseArray {
    // Method from (a): returns the sum of the entries in the one-dimensional array arr.
    public static int arraySum(int[] arr) {
        int sum = 0;
        for (int i : arr) sum += i;
        return sum;
    }

    // Method from (b): returns a one-dimensional array in which the entry at index k is the sum of the entries of row k of the two-dimensional array arr2D.
    public static int[] rowSums(int[][] arr2D) {
        int[] output = new int[arr2D.length];
        for (int i = 0; i < arr2D.length; i++) {
            output[i] = arraySum(arr2D[i]);
        }
        return output;
    }

    // Method from (c): returns true if all rows in arr2D have different row sums; false otherwise.
    public static boolean isDiverse(int[][] arr2D) {
        int[] arr2DSums = rowSums(arr2D);
        for (int i = 0; i < arr2DSums.length; i++) {
            for (int j = 0; j < i; j++) { // checking if the current element is equal to any previous element
                if (arr2DSums[i] == arr2DSums[j]) {
                    return false; // false if a dupe is found
                }
            }
        }
        return true; // true if no dupes are found
    }
    
    // main method used to verify the functionality of each method
    public static void main(String[] args) {
        // PART (a)
        System.out.println("PART (a)");
        int[] sumOf7 = {1, 2, 4}; // sum: 7
        int[] sumOf19 = {11, 3, 5}; // sum: 19
        System.out.println("arraySum test: array with a sum of 7 returns: " + arraySum(sumOf7));
        System.out.println("arraySum test: array with a sum of 19 returns: " + arraySum(sumOf19));

        // PART (b)
        System.out.println("\nPART (b)");
        int[][] diverse2DArr = {
            {2, 3, 5, 6}, // sum: 16
            {5, 9, 2, 4}, // sum: 20
            {9, 9, 8, 4}, // sum: 30
            {1, 4, 3, 2} // sum: 10
        };
        int[][] nonDiverse2DArr = {
            {2, 5, 3, 6}, // sum: 16
            {5, 9, 4, 1}, // sum: 19
            {7, 9, 4, 8}, // sum: 28
            {3, 2, 6, 5} // sum: 16
        };
        System.out.println("rowSum test: 2D array with sums [16, 20, 30, 10] returns: " + Arrays.toString(rowSums(diverse2DArr)));
        System.out.println("rowSum test: 2D array with sums [16, 19, 28, 16] returns: " + Arrays.toString(rowSums(nonDiverse2DArr)));
        
        // PART (c)
        System.out.println("\nPART (c)");
        // uses arrays from previous part in main
        System.out.println("Diverse array test: Is it diverse? " + isDiverse(diverse2DArr));
        System.out.println("Non-diverse array test: Is it diverse? " + isDiverse(nonDiverse2DArr));
    }
}

DiverseArray.main(null);
PART (a)
arraySum test: array with a sum of 7 returns: 7
arraySum test: array with a sum of 19 returns: 19

PART (b)
rowSum test: 2D array with sums [16, 20, 30, 10] returns: [16, 20, 30, 10]
rowSum test: 2D array with sums [16, 19, 28, 16] returns: [16, 19, 28, 16]

PART (c)
Diverse array test: Is it diverse? true
Non-diverse array test: Is it diverse? false

I used the main method to show the functionality of the three methods from the question.

Reflection

I decided to go part-by-part below.

Part (a)

This was a matter of basic iteration, so simple there wasn’t much struggle to speak of. I initialized an integer variable at 0 to make the sum of the elements, used a simple for-each loop to iterate through the array elements and add them to the sum, and then returned the sum. I’ve done this a bunch of times before with a one-dimensional array.

Part (b)

This was another very simple one, but there was one minor point of concern: I am not very used to initializing an array, as most of the Java programming I’ve done in the past year has worked primarily with ArrayLists. However, I thought I remembered being able to initialize an array of a given length with the code below:

int[] output = new int[arr2D.length];

The other possible point of confusion was figuring out how many rows were in a 2D array, but I was pretty confident that, since a 2D array can be visualized as an array full of rows (if you ignore verticality), I was fairly certain that arr2D.length would represent the number of rows. That also happens to be the number of sums needed to be stored in these arrays.

Because each sum corresponds to a certain row of the 2D array argument, I was able to use the same incrementing i variable from the for loop to refer to both the place for the sum in the array and the row referred to in the arraySum call. I thought this was pretty smart and efficient.

for (int i = 0; i < arr2D.length; i++) {
    output[i] = arraySum(arr2D[i]);
}

Part (c)

I’ve seen lots of different ways to look for duplicates in Java, but since the problem only specifies that the code from the Java Quick Reference is valid (which I kept open during the FRQ process), I couldn’t just use the much easier contains method (see the end of this reflection for implementation). As a result, I used a more standard method of iterating through each value and checking if the previous values in the array are equal.

for (int i = 0; i < arr2DSums.length; i++) {
    for (int j = 0; j < i; j++) { // checking if the current element is equal to any previous element
        if (arr2DSums[i] == arr2DSums[j]) {
            return false; // false if a dupe is found
        }
    }
}

I was trying to be very thoughtful about efficiency in this problem, which partially made me worried about nesting iteration through the sum array in the code above. I did, however, intentionally define the array of sums at the top of the method instead of repeatedly calling the method to calculate them. This might have been an obvious choice.

int[] arr2DSums = rowSums(arr2D);

I wish I could have used contains for this problem; it would have made it a lot simpler. I also could have used a HashSet to store sums because they can’t include duplicates, but I wasn’t sure if College Board allows for things like that.

If I had the opportunity, I would simplify the method to be written like this:

public static boolean isDiverse(int[][] arr2D) {
	int[] arr2DSums = rowSums(arr2D);
	Set<Integer> set = new HashSet<>(); // because sets cannot have duplicate values
	for (int sum : arr2DSums) {
		if (set.contains(sum)) {
			return false; // false if there are duplicate sums
		} else {
			set.add(sum);
		}
	}
	return true; // true if no duplicates are found
}

Overall Notes and Reminders

  • The .length of a 2D array represents the number of rows it contains, assuming it is formatted in the typical [r][c] fashion.
  • If corresponding indexes between multiple arrays are being accessed in the same iterative process, it may be a good idea to use a standard iterating variable for loop to iterate so that it can be applied to all relevant arrays.
  • When initializing an array, the statement is formatted as follows: type[] varName = new type[arrayLength]
  • When an integer array is initialized to a certain length, each of those elements will be 0 initially, so they can be incremented immediately from 0.
  • A HashSet is best used in a situation where a collection of elements must not contain duplicates and its order doesn’t matter. A LinkedHashSet preserves order, however, and thus may be preferable in some cases.