Topic 4.15 — Developing Algorithms Using 2D Arrays
Goal: apply common algorithm patterns to 2D arrays (searching, counting, finding max/min, updating), and handle boundaries correctly when working with neighbors.
The big idea
2D array algorithms are mostly “regular” algorithms plus: nested loops and careful bounds.
Always decide your traversal first (row-major is the default), then implement the pattern (sum/count/search/update).
Algorithm patterns you should recognize
| Pattern | What you do |
|---|---|
| Sum / average | accumulate values as you traverse |
| Count | increment when a condition is true |
| Find max/min | keep a “best so far” variable |
| Search | check each element until found |
| Update/replace | assign back to grid[r][c] |
| Neighbor-based | use r±1, c±1 with bounds checks |
Sum / average
int sum = 0;
int count = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
sum += grid[r][c];
count++;
}
}
double avg = (double) sum / count;
Count with a condition
Example: count values that are even.
int evens = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
if (grid[r][c] % 2 == 0) evens++;
}
}
Find max / min
Assume the array has at least one element.
int max = grid[0][0];
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
if (grid[r][c] > max) {
max = grid[r][c];
}
}
}
Search for a target
boolean found = false;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
if (grid[r][c] == target) {
found = true;
}
}
}
(You can also return early in a method if allowed.)
Update / replace
Example: replace all negatives with 0.
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
if (grid[r][c] < 0) {
grid[r][c] = 0;
}
}
}
Neighbor-based algorithms (boundaries matter)
If you look at “right neighbor” [r][c+1], don’t let c reach the last column.
// Count how many times a value is less than the value to its right
int incRight = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length - 1; c++) { // -1 so c+1 is safe
if (grid[r][c] < grid[r][c + 1]) {
incRight++;
}
}
}
Neighbor example: “down” neighbor
For [r+1][c], stop r at the second-to-last row.
int downPairs = 0;
for (int r = 0; r < grid.length - 1; r++) { // -1 so r+1 is safe
for (int c = 0; c < grid[r].length; c++) {
// if rectangular, this is safe; for jagged you'd need extra checks
if (grid[r][c] == grid[r + 1][c]) {
downPairs++;
}
}
}
Jagged arrays + neighbors = extra caution
If rows have different lengths, grid[r+1][c] might not exist even when r+1 exists.
// Safe-ish approach for jagged arrays when comparing down-neighbors:
for (int r = 0; r < grid.length - 1; r++) {
int cols = Math.min(grid[r].length, grid[r + 1].length);
for (int c = 0; c < cols; c++) {
// grid[r][c] and grid[r+1][c] are both valid
}
}
Quick self-check
- What traversal pattern is most common for 2D arrays?
- Which variable usually tracks rows? columns?
- How do you safely compare a cell to its right neighbor?
- How do you safely compare a cell to its down neighbor?
- Why do jagged arrays require extra care for neighbor algorithms?