Implementing Array Algorithms
Why this matters
Imagine you're the manager of your school's basketball team, the Scorpions. You have a list of points scored by each player in the last game: [12, 5, 21, 8, 15, 2, 21]. The coach asks you a few questions: Who was the top scorer? What was the team's average? Did more than one player score exactly 21 points?
Answering these questions by hand is easy for a small team. But what if you were analyzing data for the entire NBA? You can't just eyeball it. You need a systematic, repeatable process.
That's exactly what array algorithms are. They are the fundamental recipes you'll use in programming to sift through collections of data and find meaningful answers. In this lesson, we'll learn the standard patterns for finding max values, calculating sums, spotting duplicates, and more.
Concept overview
flowchart TD
A[Start] --> B{Initialize max = array[0]};
B --> C{Initialize i = 1};
C --> D{Is i < array.length?};
D -- Yes --> E{Is array[i] > max?};
E -- Yes --> F{max = array[i]};
E -- No --> G;
F --> G{i = i + 1};
G --> C;
D -- No --> H[Return max];
H --> I[End];
Core explanation
Hello there! I'm Saavi, and I'm excited to walk you through some of the most useful tools in your programming toolkit: standard array algorithms.
Think of an array like a long street of houses, and each house has an address (its index) and something inside (its value). An "array traversal" is like taking a walk down that street, starting at house 0 and visiting each one in order until the end. The algorithms we'll learn today are just different tasks you can perform on that walk.
Let's get started.
Finding a Minimum or Maximum Value
This is the "king of the hill" algorithm. To find the highest score in a list, you'd start by assuming the very first score is the highest. Then, you walk through the rest of the list. If you find a score that's higher than your current "king," you replace it. By the time you reach the end, you're left with the true highest score.
int[] scores = {88, 92, 75, 100, 95};
// Assume the first element is the max
int maxScore = scores[0];
// Start loop at the SECOND element (index 1)
for (int i = 1; i < scores.length; i++) {
[[fig:max_score_trace]]
if (scores[i] > maxScore) {
maxScore = scores[i]; // New king of the hill!
}
}
// After the loop, maxScore will be 100
Computing a Sum or Average
This is like being a cashier at a grocery store. You start with your register at $0.00 (an "accumulator" variable) and add the price of each item one by one.
To find a sum, you initialize a variable (e.g., sum) to 0. Then, you loop through the array and add each element to sum.
double[] prices = {12.50, 4.25, 23.00};
double sum = 0.0;
for (int i = 0; i < prices.length; i++) {
[[fig:sum_average_trace]]
sum = sum + prices[i]; // or sum += prices[i];
}
// To get the average, just divide by the number of items.
// But watch out for integer division if your numbers are ints!
double average = sum / prices.length;
Checking for Properties
Sometimes you don't need a number, but a true or false answer.
1. Does at least one element have a property?
(e.g., "Does any player have a perfect score?")
You can loop through and if you find one, you can stop and return true immediately. If you get all the way to the end without finding one, you can return false.
// Check if any score is 100
boolean foundPerfectScore = false;
for (int score : scores) { // Using an enhanced for-loop
if (score == 100) {
foundPerfectScore = true;
break; // Found it! No need to keep looking.
}
}
2. Do all elements have a property?
(e.g., "Did every student pass the test?")
This is the opposite. You assume the answer is true and look for a reason to say false. If you find even one element that doesn't have the property, you can stop and return false.
// Check if all scores are passing (>= 60)
boolean allPassed = true;
for (int score : scores) {
if (score < 60) {
allPassed = false;
break; // Found a failing score, so not all passed.
}
}
Counting Elements with a Property
This is a blend of summing and checking a property. You create a counter variable, initialize it to 0, and loop through the array. If an element meets your criteria, you increment the counter.
// Count how many scores are A's (>= 90)
int numAs = 0;
for (int score : scores) {
if (score >= 90) {
numAs++;
}
}
Accessing Consecutive Pairs
What if you want to find the biggest jump in temperature between two consecutive days? You need to compare temps[0] with temps[1], then temps[1] with temps[2], and so on.
The key here is that your loop must stop one element early to avoid falling off the end of the array! If you try to access temps[i+1] when i is the last index, you'll get an ArrayIndexOutOfBoundsException.
int[] temps = {72, 75, 68, 79, 82};
// Loop stops at temps.length - 1
for (int i = 0; i < temps.length - 1; i++) {
int temp1 = temps[i];
int temp2 = temps[i+1];
System.out.println("Comparing " + temp1 + " and " + temp2);
}
Finding Duplicates
A common way to check for duplicates is with a nested loop. The outer loop picks an element, and the inner loop checks all the subsequent elements to see if they match.
String[] names = {"Maya", "Carlos", "Priya", "Maya"};
boolean hasDuplicates = false;
for (int i = 0; i < names.length; i++) {
for (int j = i + 1; j < names.length; j++) {
if (names[i].equals(names[j])) { // Use .equals() for objects!
hasDuplicates = true;
break; // Found a duplicate, can stop inner loop
}
}
if (hasDuplicates) {
break; // Can stop outer loop too
}
}
Shifting, Rotating, and Reversing
These algorithms actually change the array.
Shifting: To shift all elements left, you can copy arr[1] into arr[0], arr[2] into arr[1], and so on. You usually need to save the first element beforehand if you want to "rotate" it to the end.
// Rotate all elements one spot to the left
int[] nums = {10, 20, 30, 40};
int first = nums[0]; // Save the first element
for (int i = 0; i < nums.length - 1; i++) {
nums[i] = nums[i+1]; // Shift left
}
nums[nums.length - 1] = first; // Put the first element at the end
// nums is now {20, 30, 40, 10}
Reversing: The most efficient way to reverse an array in place is to use two "pointers" or indices. One starts at the beginning (start) and one at the end (end). You swap their elements, then move the pointers toward the middle (start++, end--) until they meet.
int[] data = {1, 2, 3, 4, 5};
int start = 0;
int end = data.length - 1;
while (start < end) {
// Swap elements
int temp = data[start];
data[start] = data[end];
data[end] = temp;
// Move pointers
start++;
end--;
}
// data is now {5, 4, 3, 2, 1}
These patterns are your building blocks. Once you master them, you can combine them to solve almost any array problem the AP exam throws at you.
See it in action
Worked examples
Let's put these patterns into practice with a couple of examples.
Calculating Average Sale Price
Problem: You have an array of recent home sale prices in Dallas. Some entries are -1, indicating a data error. Write a method averageSalePrice that calculates the average price of only the valid sales (the positive numbers).
public double averageSalePrice(double[] prices) {
// ... your code here ...
}
Step-by-Step Solution:
- 1Identify the goalWe need an average. An average is
sum / count. This tells us we need to find two things: the sum of the valid prices and the count of valid prices. - 2Initialize accumulatorsWe need a variable to hold the running total and another to hold the count. Let's initialize both to zero before the loop.
double sum = 0.0; int count = 0; - 3Traverse the arrayWe need to look at every price, so a standard
forloop is perfect.for (int i = 0; i < prices.length; i++) { // ... logic inside the loop ... } - 4Apply the conditionInside the loop, we only care about prices that are not
-1. So, we'll use anifstatement to check each price.if (prices[i] > 0) { // or prices[i] != -1 // It's a valid price! sum += prices[i]; // Add it to our sum count++; // Increment our count } - 5Calculate and return the resultAfter the loop finishes,
sumandcountwill hold the final totals. We can now calculate the average. We also need to consider the edge case: what if there are no valid sales? In that case,countwould be 0, and dividing by zero is a crash. We should handle that.
Final Code:
public double averageSalePrice(double[] prices) {
double sum = 0.0;
int count = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] > 0) {
sum += prices[i];
count++;
}
}
// This is a critical check!
if (count == 0) {
return 0.0; // Or handle as an error, but 0.0 is a safe return.
}
return sum / count;
}
Where students go wrong: Many students forget the if (count == 0) check. On the AP exam, test cases will almost certainly include an empty array or an array with no valid data. Forgetting this check leads to a program crash (or NaN for floating-point division) and lost points.
Shifting Right
Problem: Write a method that shifts all elements in an integer array one position to the right. The last element should "wrap around" and become the new first element. For example, {1, 2, 3, 4} becomes {4, 1, 2, 3}.
Step-by-Step Solution:
- 1Identify the challengeIf we just start copying
nums[i]tonums[i+1], we'll overwrite the values we need later. For example, if we copynums[0]tonums[1], the original value ofnums[1]is lost forever. - 2The key insightWe need to save the element that will be overwritten first. In a right shift, that's the last element. It needs to be put in
nums[0]at the end.int last = nums[nums.length - 1]; - 3Work backwardsTo avoid overwriting, we should shift from right to left. We'll copy
nums[2]tonums[3], thennums[1]tonums[2], and so on. This means our loop should start at the end and go down.// Start from the second-to-last element and move it to the last spot for (int i = nums.length - 2; i >= 0; i--) { nums[i + 1] = nums[i]; } - 4Place the saved elementNow that everything has been shifted, the first spot (
nums[0]) is open. We can place thelastelement we saved in step 2 into that spot.nums[0] = last;
Final Code:
public void shiftRight(int[] nums) {
// Handle edge case of empty or single-element array
if (nums.length < 2) {
return;
}
// 1. Save the last element
int last = nums[nums.length - 1];
// 2. Shift everything from right to left
for (int i = nums.length - 2; i >= 0; i--) {
nums[i + 1] = nums[i];
}
// 3. Place the saved element at the front
nums[0] = last;
}Try it yourself
Ready to try on your own? Here are a couple of challenges. Don't worry about writing the full class, just focus on the logic inside the method.
Problem 1: isSorted
Write a boolean method isSorted that takes an array of integers and returns true if the array is sorted in ascending (non-decreasing) order, and false otherwise. For example, {10, 20, 20, 30} is sorted, but {10, 30, 20} is not.
- HintThis involves checking consecutive pairs. You're looking for any place where
array[i]is greater thanarray[i+1]. If you find even one, you know the answer isfalse. - HintWhat should your loop bounds be?
Problem 2: countDuplicates
Write a method countDuplicates that takes an array of String objects and returns the number of strings that appear more than once. For example, in {"A", "B", "A", "C", "B", "A"}, the strings "A" and "B" are duplicates, so the method should return 2.
- HintThis is a tough one! A nested loop is a good place to start. But how do you avoid counting the same duplicate string multiple times?
- HintConsider sorting the array first. How might that make counting duplicates easier? (This is a more advanced approach, but great practice!)
Practice — 8 questions
In simple terms, array algorithms are about writing step-by-step recipes (code) to find, count, change, or rearrange items in a list of data.
int[] scores = {88, 92, 75, 100, 95};
// Assume the first element is the max
int maxScore = scores[0];
// Start loop at the SECOND element (index 1)
for (int i = 1; i < scores.length; i++) {
[[fig:max_score_trace]]
if (scores[i] > maxScore) {
maxScore = scores[i]; // New king of the hill!
}
}
// After the loop, maxScore will be 100
- 4.5.A: Develop code for standard and original algorithms for a particular context or specification that involves arrays and determine the result of these algorithms.
- 4.5.A.1
- There are standard algorithms that utilize array traversals to: • determine a minimum or maximum value • compute a sum or average • determine if at least one element has a particular property • determine if all elements have a particular property • determine the number of elements having a particular property • access all consecutive pairs of elements • determine the presence or absence of duplicate elements • shift or rotate elements left or right • reverse the order of the elements
flowchart TD
A[Start] --> B{Initialize max = array[0]};
B --> C{Initialize i = 1};
C --> D{Is i < array.length?};
D -- Yes --> E{Is array[i] > max?};
E -- Yes --> F{max = array[i]};
E -- No --> G;
F --> G{i = i + 1};
G --> C;
D -- No --> H[Return max];
H --> I[End];
Read what Saavi narrates
Hi there, I'm Saavi, and welcome to Shrutam.
Imagine you're the manager of your school's basketball team. You have a list of points scored by each player in the last game... something like 12, 5, 21, 8, and so on. The coach asks you, "Who was our top scorer?"
Answering that is easy for a small list. But what if you were analyzing data for the entire NBA? You need a systematic process. That's exactly what array algorithms are. They are standard, reusable patterns for processing data stored in an array. By looping through the array, we can perform common tasks like finding the highest value, calculating an average, or rearranging the elements.
Let's take that "top scorer" problem. In code, this is the "find the maximum" algorithm. It's like a game of king of the hill. First, you just assume the very first player's score is the highest. Let's say it's 12. So, 12 is the king of the hill for now.
Then, you walk through the rest of the list. The next score is 5. Is 5 greater than 12? No. So 12 stays king. The next score is 21. Is 21 greater than 12? Yes! So, you have a new king of the hill: 21. You continue this process until you've checked every score. The last one standing is your true maximum.
This is a really common pattern, but here's where students often make a mistake. When finding a maximum, many people initialize their max variable to zero. This seems safe, but what if you were tracking temperature changes in Boston in January, and all the temperatures were negative? If your array was negative 10, negative 5, negative 18... your code would incorrectly say the max temperature was zero.
The correct way is to always initialize your max variable to the very first element of the array. That way, your starting point is a real value from your data set, and the algorithm works every time.
These little patterns are the building blocks for so much of computer science. Once you get comfortable with them, you'll see them everywhere. You can do this!
The last valid index in an array is `array.length - 1`. Using `<=` will cause an `ArrayIndexOutOfBoundsException` when `i` equals `array.length`.
Always use `i < array.length` for a standard forward traversal.
Code like `int max = nums[0];` will crash if `nums` is an empty array (`nums.length` is 0).
Add a check at the beginning of your method, like `if (nums.length == 0) { return; /* or some default value */ }`.
This fails if all numbers in the array are negative (for `max`) or all numbers are larger than your initial `min`. The code will return your incorrect initial value.
Initialize `max` or `min` to the first element of the array: `int max = nums[0];`. This is guaranteed to work for any range of numbers.
Looping to `i < array.length` and then accessing `array[i+1]` will cause an `ArrayIndexOutOfBoundsException` on the last iteration.
Stop the loop one element early: `for (int i = 0; i < array.length - 1; i++)`.
`==` checks if two references point to the exact same object in memory, not if their contents are the same. `"hello"` from user input and `"hello"` in your code might be two different objects with the same value.
Always use the `.equals()` method to compare the contents of objects: `if (name1.equals(name2))`.
If `sum` and `count` are both integers, the expression `sum / count` will perform integer division, truncating any decimal part. `7 / 2` will be `3`, not `3.5`.
Cast one of the numbers to a double before dividing: `double average = (double) sum / count;`.