CompSci.rocks
FRQcsapa

Round: 2025 FRQ 3

A step-by-step solution to the 2025 AP CSA FRQ 3 (Round), covering building an ArrayList from a String array and pairing elements from opposite ends toward the middle in Java.

Pairing up tournament competitors by rank — best against worst, and working inward — is the core idea in this AP Computer Science A free-response question, starting with turning a plain array of names into a ranked ArrayList and ending with generating every match for the next round.

What This FRQ Tests

  • AP CSA units: Unit 6 (Array) and Unit 7 (ArrayList)
  • Core skill: building an ArrayList of custom objects from a String[], assigning each one a value based on its position
  • Secondary skill: walking two indices toward each other from opposite ends of a list at the same time
  • Official category: "Array/ArrayList" — always FRQ 3 on the AP CSA exam

The Setup

  • The given Competitor class (not modified) stores a name and rank.
  • The given Match class (not modified) pairs two Competitor objects together.
  • Round holds:
    • private ArrayList<Competitor> competitorList
  • You're asked to write the constructor and one method:
    • Round(String[] names) — builds competitorList, ranking competitors 1 through n in the order names lists them
    • ArrayList<Match> buildMatches() — pairs up competitors for the next round: best-vs-worst, second-best-vs-second-worst, and so on; if there's an odd number of competitors, the single best-ranked one sits out

Part (a): Writing the Round Constructor

The Rule, Broken Down

  1. Every name in names becomes one Competitor object.
  2. Competitors appear in competitorList in the same order they appear in names.
  3. Rank is based on position: the first name is rank 1, and rank increases by 1 for each name after that.

Step-by-Step Approach

  1. Initialize competitorList to a new, empty ArrayList<Competitor>.
  2. Loop over every index of names.
  3. At each index, construct a Competitor using that name and a rank of "index plus one" (since ranks start at 1, not 0).
  4. Add that Competitor to competitorList.

The Code

public Round(String[] names)
{
    competitorList = new ArrayList<Competitor>();

    for (int i = 0; i < names.length; i++)
    {
        competitorList.add(new Competitor(names[i], i + 1));
    }
}

Why Each Piece Matters

  • i + 1 for the rank, not i — array indices start at 0, but the problem defines rank 1 as the best, so the rank has to be shifted up by one relative to the index.
  • competitorList.add(...) inside the loop, in index order — this is what guarantees the final list's order matches names's order exactly, without needing any extra sorting step.
  • A brand-new Competitor object per namecompetitorList holds Competitor objects, not raw Strings, so each name has to be wrapped before it can be added.

Tracing the Example

Using String[] players = {"Alex", "Ben", "Cara"}:

Index Name Rank assigned
0 "Alex" 1
1 "Ben" 2
2 "Cara" 3

Matches the question's expected contents of competitorList exactly.

Common Mistakes to Avoid

  • Using the raw index i as the rank instead of i + 1 — this would incorrectly start ranking at 0.
  • Looping over competitorList instead of names — at the start of the constructor, competitorList is empty; the array being read from is names.
  • Adding the raw String instead of a new Competitor(...) — this wouldn't compile, since competitorList is typed as ArrayList<Competitor>.

Part (b): Writing buildMatches()

The Rule, Broken Down

  1. If there's an even number of competitors, pair the best with the worst, the second-best with the second-worst, and so on, working inward.
  2. If there's an odd number of competitors, the single best-ranked competitor sits out, and everyone else pairs up the same way.
  3. Every pair becomes one Match object added to the returned ArrayList.

Step-by-Step Approach

  1. Figure out where to start the pairing: index 0 if the list's size is even, or index 1 if it's odd (skipping the best-ranked competitor).
  2. Also track an index starting at the last position in the list.
  3. Loop while the "start" index is still less than the "end" index: pair those two competitors into a Match, then move the start index forward and the end index backward.
  4. Stop once the two indices meet or cross — every competitor has been paired (or, for an odd list, correctly left out).

The Code

public ArrayList<Match> buildMatches()
{
    ArrayList<Match> matches = new ArrayList<Match>();
    int left;

    if (competitorList.size() % 2 == 0)
    {
        left = 0;
    }
    else
    {
        left = 1;
    }

    int right = competitorList.size() - 1;

    while (left < right)
    {
        matches.add(new Match(competitorList.get(left), competitorList.get(right)));
        left++;
        right--;
    }

    return matches;
}

Why Each Piece Matters

  • left starts at 1 for an odd-sized list, 0 for an even-sized one — this single decision is what correctly excludes the best-ranked competitor only when there's an odd number of competitors, without needing a separate special case anywhere else in the method.
  • right starts at competitorList.size() - 1, the last valid index — pairing always starts from the worst-ranked competitor working backward.
  • left < right as the loop condition — this naturally stops the moment the two pointers meet (an exact middle element with nothing left to pair, which can't happen here since a leftover single element only occurs in an odd list and was already skipped) or cross (every remaining competitor has been paired).
  • left++ and right-- together, once per pair — each pass through the loop consumes exactly one competitor from each end.

Tracing the Example

Using the odd example — competitorList of Alex(1), Ben(2), Cara(3):

  • Size is 3 (odd) → left starts at 1, right starts at 2.
  • left < right (1 < 2): pair index 1 (Ben) with index 2 (Cara) → add Match(Ben, Cara). left becomes 2, right becomes 1.
  • left < right? 2 < 1 is false → loop ends.

Result: one match, between Ben and Cara — matches the question exactly (Alex, the best-ranked, correctly sits out).

Using the even example — Rei(1), Sam(2), Vi(3), Tim(4):

  • Size is 4 (even) → left starts at 0, right starts at 3.
  • Pair index 0 (Rei) with index 3 (Tim) → add Match(Rei, Tim). left becomes 1, right becomes 2.
  • left < right (1 < 2): pair index 1 (Sam) with index 2 (Vi) → add Match(Sam, Vi). left becomes 2, right becomes 1.
  • Loop ends.

Result: two matches, (Rei, Tim) and (Sam, Vi) — matches the question exactly, in the same order given.

Common Mistakes to Avoid

  • Always starting left at 0, even for an odd-sized list — this would incorrectly include the best-ranked competitor in a match instead of letting them sit out.
  • Using left <= right instead of left < right. For an odd-sized list (after skipping the first competitor), the remaining competitors always pair up evenly, so left and right should never land on the exact same index — but using <= would risk pairing a competitor with themselves if that assumption were ever violated.
  • Modifying competitorList itself (e.g., removing paired competitors) instead of just reading from it — the postcondition explicitly requires competitorList to be left unchanged.

Key Takeaways

  • Converting positions in an array to a 1-indexed value (like rank) is a simple + 1 on the loop index — easy to get right, easy to accidentally skip.
  • "Pair the outermost elements, working inward" is a two-pointer pattern: one index starting at the front, one at the back, moving toward each other one step at a time until they meet or cross.
  • A single conditional choice of where a pointer starts (rather than a separate code path) is often enough to handle an "odd one out" case cleanly, without duplicating the main pairing logic.

Related FRQs