Sliding Window Maximum Average Subarray in Java DSA
-
Last Updated: August 13, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Solve Sliding Window Maximum Average Subarray in Java DSA in three ways — brute force, prefix sum, and sliding window — with full step-by-step dry runs and clean code.
The Sliding Window pattern is one of the most useful tricks for array problems, and Maximum Average Subarray I in Java is the perfect place to learn it. The problem is small. You get an array of numbers and a window size k. Your job is to find the block of k numbers in a row that has the highest average.
So if the array is [1, 12, -5, -6, 50, 3, 30] and k is 4, you look at every group of 4 numbers next to each other. Then you pick the group with the biggest average and return that number.
There is one small thing to notice. The average of a fixed group is just its sum divided by k. Since k never changes, the group with the biggest sum also has the biggest average. So we can chase the biggest sum and divide once at the end.
We will build the answer in three steps, like always. The brute force recomputes each window sum from scratch. The prefix sum trick makes each window sum a quick subtraction. Then the sliding window slides one window across the array and reuses the old sum.
Every approach gets a full, step-by-step dry run on the same seven numbers. Nothing is skipped, so you can watch the sum change at each move and see exactly why one window wins.
Let us pin down the rules before we write any code.
For our array [1, 12, -5, -6, 50, 3, 30] with k = 4, the best window is [-6, 50, 3, 30]. Its sum is 77, so its average is 77 divided by 4, which is 19.25. No other window of length 4 beats that.
Every window here has the same length k. So dividing by k treats all windows the same way. Because of that, the window with the largest sum is also the window with the largest average.
The slow way finds each window sum on its own, adding k numbers every time. Notice that two windows next to each other share most of their numbers. So instead of adding everything again, you can drop the number that left and add the number that entered.
Try every starting spot for the window. For each start, add up the k numbers in that window from scratch. Keep the biggest sum you have seen. It is the plainest idea, and it works, but it redoes a lot of adding.
best = negative infinity
for start = 0 to n - k:
windowSum = 0
for j = start to start + k - 1:
windowSum = windowSum + nums[j]
if windowSum > best:
best = windowSum
return best / kThe plan is simple. Look at every window, add its numbers, and remember the largest sum.
public class MaxAverageBrute {
public static double findMaxAverage(int[] nums, int k) {
int n = nums.length;
double best = Double.NEGATIVE_INFINITY;
for (int start = 0; start <= n - k; start++) {
int windowSum = 0;
for (int j = start; j < start + k; j++) {
windowSum += nums[j];
}
if (windowSum > best) {
best = windowSum;
}
}
return best / k;
}
public static void main(String[] args) {
int[] nums = {1, 12, -5, -6, 50, 3, 30};
System.out.println(findMaxAverage(nums, 4)); // 19.25
}
}This is the same plan, written in Java. We start best very low so the first window always wins the first compare.
Let us trace nums = [1, 12, -5, -6, 50, 3, 30] with k = 4. The array has 7 numbers, so a window of size 4 can start at index 0, 1, 2, or 3. That gives us four windows to check. best begins at negative infinity.
This table shows each window, the numbers inside it, the fresh sum we add up, and whether it beats best.
| start | window (the 4 numbers) | windowSum (added fresh) | best after this window |
|---|---|---|---|
| 0 | [1, 12, -5, -6] | 1 + 12 + (-5) + (-6) = 2 | 2 (new best) |
| 1 | [12, -5, -6, 50] | 12 + (-5) + (-6) + 50 = 51 | 51 (new best) |
| 2 | [-5, -6, 50, 3] | (-5) + (-6) + 50 + 3 = 42 | 51 (best stays) |
| 3 | [-6, 50, 3, 30] | (-6) + 50 + 3 + 30 = 77 | 77 (new best) |
After all four windows, best holds 77. We divide by k, so 77 divided by 4 gives 19.25. That is the answer.
Let us walk each window and see exactly what the brute force does at every start.
start = 0, window [1, 12, -5, -6].
The inner loop begins fresh and adds all four numbers of this first window.
best was negative infinity, so 2 easily beats it. Now best is 2.
start = 1, window [12, -5, -6, 50].
The window shifts right by one, and the inner loop adds all four numbers again from scratch.
Notice the waste here. We added -5 and -6 in the last window too, yet we added them again.
start = 2, window [-5, -6, 50, 3].
Again the inner loop adds all four fresh numbers of this window.
This window looked promising thanks to the 50, but the two negatives at the front held it back.
start = 3, window [-6, 50, 3, 30].
This is the last window, since start cannot go past index 3 here.
This window drops the deep negatives from earlier and keeps three strong numbers, so it wins. After the loop ends, best is 77 and the average is 19.25.
Interview Insight A common opener is “what is the time cost of the plain version?” Say O(n times k). You scan n windows and re-add k numbers for each one, which is wasteful when the same numbers keep repeating.
The result is correct, but the repeated adding hurts on long arrays. Next we cut that repeated work with a prefix sum.
Build a prefix array first. Slot i of the prefix holds the sum of all numbers before index i. Once you have it, any window sum becomes one subtraction. The sum from start to start plus k is just prefix[start + k] minus prefix[start].
prefix = array of size n + 1, all zeros
for i = 0 to n - 1:
prefix[i + 1] = prefix[i] + nums[i]
best = negative infinity
for start = 0 to n - k:
windowSum = prefix[start + k] - prefix[start]
if windowSum > best:
best = windowSum
return best / kThink of prefix as a running total that never resets. Each slot stores the total up to that point.
public class MaxAveragePrefix {
public static double findMaxAverage(int[] nums, int k) {
int n = nums.length;
long[] prefix = new long[n + 1];
for (int i = 0; i < n; i++) {
prefix[i + 1] = prefix[i] + nums[i];
}
double best = Double.NEGATIVE_INFINITY;
for (int start = 0; start <= n - k; start++) {
long windowSum = prefix[start + k] - prefix[start];
if (windowSum > best) {
best = windowSum;
}
}
return best / k;
}
public static void main(String[] args) {
int[] nums = {1, 12, -5, -6, 50, 3, 30};
System.out.println(findMaxAverage(nums, 4)); // 19.25
}
}The Java version follows the same two loops. We use long for prefix so big arrays do not overflow.
Same input, nums = [1, 12, -5, -6, 50, 3, 30] and k = 4. First we build the prefix array. It has 8 slots, since n is 7. Slot 0 stays at 0, and each next slot adds one more number.
This first table builds prefix one number at a time.
| i (number index) | nums[i] | prefix[i + 1] = prefix[i] + nums[i] | prefix so far |
|---|---|---|---|
| 0 | 1 | prefix[1] = 0 + 1 | 1 |
| 1 | 12 | prefix[2] = 1 + 12 | 13 |
| 2 | -5 | prefix[3] = 13 + (-5) | 8 |
| 3 | -6 | prefix[4] = 8 + (-6) | 2 |
| 4 | 50 | prefix[5] = 2 + 50 | 52 |
| 5 | 3 | prefix[6] = 52 + 3 | 55 |
| 6 | 30 | prefix[7] = 55 + 30 | 85 |
So the finished prefix array is [0, 1, 13, 8, 2, 52, 55, 85]. Now each window sum is just one subtraction. best starts at negative infinity.
This second table finds each window sum by subtracting two prefix slots.
| start | windowSum = prefix[start + k] – prefix[start] | value | best after this window |
|---|---|---|---|
| 0 | prefix[4] – prefix[0] = 2 – 0 | 2 | 2 (new best) |
| 1 | prefix[5] – prefix[1] = 52 – 1 | 51 | 51 (new best) |
| 2 | prefix[6] – prefix[2] = 55 – 13 | 42 | 51 (best stays) |
| 3 | prefix[7] – prefix[3] = 85 – 8 | 77 | 77 (new best) |
best ends at 77, so the average is 77 divided by 4, which is 19.25. Same answer as before, but each window took one subtraction instead of four additions.
Let us split this into the build pass and the query pass.
Building the prefix array.
This pass just keeps a running total and stores it after each number.
Now prefix holds the total up to every point, which is all we need for fast window sums.
Querying each window.
Each window sum is the running total at its end minus the running total at its start.
The subtraction works because the part before the window cancels out, leaving only the numbers inside it. best finishes at 77, so the average is 19.25.
Interview Insight If asked “why does prefix[start + k] minus prefix[start] give the window sum?”, explain that the first term is the total up to the window end, and the second is the total up to the window start. Subtracting removes everything before the window.
This is much faster than brute force. Still, it needs an extra array. The sliding window gets the same speed while using almost no extra space.
Add the first k numbers to get the first window sum. Then slide the window one step at a time. At each step, add the number that enters on the right and subtract the number that leaves on the left. Track the biggest sum as you go. No extra array, and each number is touched about once.
windowSum = 0
for i = 0 to k - 1:
windowSum = windowSum + nums[i]
best = windowSum
for i = k to n - 1:
windowSum = windowSum + nums[i] - nums[i - k]
if windowSum > best:
best = windowSum
return best / kPicture a frame exactly k boxes wide sitting over the array. The whole trick is that when the frame moves one step, only the two edge boxes change. Everything in the middle stays put, so we never re-add it. Let us read the pseudocode in three small phases.
Phase 1: build the first window.
The first loop fills windowSum with the first k numbers. This is the only time we add k numbers the slow way. After it, windowSum holds the sum of the window that sits at the very start of the array.
Phase 2: seed the best value.
Right after building, we set best to windowSum. We have only seen one window so far, so that window is the best by default. There is no comparison to make yet.
Phase 3: slide and update.
The second loop is where the real work happens, and it is short. Here index i points at the number entering on the right. Meanwhile the number leaving on the left always sits k spots behind it, at index i minus k.
public class MaxAverageSliding {
public static double findMaxAverage(int[] nums, int k) {
int n = nums.length;
long windowSum = 0;
for (int i = 0; i < k; i++) {
windowSum += nums[i];
}
long best = windowSum;
for (int i = k; i < n; i++) {
windowSum += nums[i] - nums[i - k];
if (windowSum > best) {
best = windowSum;
}
}
return (double) best / k;
}
public static void main(String[] args) {
int[] nums = {1, 12, -5, -6, 50, 3, 30};
System.out.println(findMaxAverage(nums, 4)); // 19.25
}
}The Java code has two short loops and no extra array. The one trick is the index i minus k, which points at the number leaving on the left.
Same input again, nums = [1, 12, -5, -6, 50, 3, 30] and k = 4. First we build the starting window from the first four numbers. This table shows that build step by step.
| i (build index) | nums[i] added | windowSum after adding |
|---|---|---|
| 0 | 1 | 0 + 1 = 1 |
| 1 | 12 | 1 + 12 = 13 |
| 2 | -5 | 13 + (-5) = 8 |
| 3 | -6 | 8 + (-6) = 2 |
So the first window [1, 12, -5, -6] has sum 2. We set best to 2. Now the slide begins at index 4. Each step adds nums[i] on the right and removes nums[i – k] on the left.
Legend: “enters” is nums[i], the new right number. “leaves” is nums[i – k], the old left number that slides out.
| i (slide index) | enters nums[i] | leaves nums[i – k] | windowSum = old + enters – leaves | best after step |
|---|---|---|---|---|
| 4 | 50 | nums[0] = 1 | 2 + 50 – 1 = 51 | 51 (new best) |
| 5 | 3 | nums[1] = 12 | 51 + 3 – 12 = 42 | 51 (best stays) |
| 6 | 30 | nums[2] = -5 | 42 + 30 – (-5) = 77 | 77 (new best) |
After the last slide, best is 77. We divide by k, so 77 divided by 4 is 19.25. Same answer, and we touched each number only about once.
Let us walk the whole run one move at a time. First we build the starting window, then we slide it three times. At every move, watch two things: which number enters, and which number leaves.
Building the first window (indexes 0 to 3).
This first loop is the only slow part, and it runs just once. It adds the first four numbers into windowSum, which starts empty at 0.
The starting window is [1, 12, -5, -6] and its sum is 2. This is the only window we have looked at, so best begins at 2. From here on, we never add four numbers again. Each slide changes only two.
Slide 1, at i = 4: 50 enters, 1 leaves.
The frame shifts right by one. It used to cover indexes 0 to 3, and now it covers indexes 1 to 4. So the new window is [12, -5, -6, 50].
Notice what we did not do. We never re-added 12, -5, or -6, even though they are all inside the new window. They stayed inside the frame, so their contribution was already in windowSum. We only adjusted the two edges, and that is what makes each slide cheap.
Slide 2, at i = 5: 3 enters, 12 leaves.
The frame moves right again, now covering indexes 2 to 5. The window becomes [-5, -6, 50, 3].
This slide lost value. We dropped a strong 12 and only gained a small 3, so the window sum fell from 51 to 42. That is fine. We still keep best at 51, remembering the better window we saw earlier.
Slide 3, at i = 6: 30 enters, -5 leaves.
This is the final slide, since index 6 is the last spot in the array. The frame now covers indexes 3 to 6, so the window is [-6, 50, 3, 30].
This slide gained twice over. It brought in a big 30 and, by dropping the -5, it removed a number that was dragging the sum down. Both changes push the sum up, so this window ends up the strongest of them all.
Wrapping up.
The loop is now done, and best holds 77. We divide by k, so 77 divided by 4 gives 19.25, which is the answer. Look back at the whole run: we added four numbers once at the start, then did just one add and one subtract per slide. That is why the sliding window is fast even when k is large.
Interview Insight Interviewers often ask “why is the slide O(1) per step?” Because each step only does one add and one subtract, no matter how large k is. The numbers already inside the window are never touched again.
| Approach | How each window sum is found | Extra memory | Speed feel |
|---|---|---|---|
| Brute force | Add all k numbers again | None | Slowest of the three |
| Prefix sum | One subtraction of two slots | One prefix array | Fast, but uses an array |
| Sliding window | Add one, subtract one | None | Fast and leanest |
All three return the same answer, 19.25. They just pay different prices to get there.
| Approach | Time | Space | Note |
|---|---|---|---|
| Brute force | O(n times k) | O(1) | Simple, but re-adds the same numbers |
| Prefix sum | O(n) | O(n) | Fast, needs an extra array |
| Sliding window | O(n) | O(1) | Fast and lean, the expected answer |
Prefix sum and sliding window share the same linear time. What sets them apart is memory. The prefix version keeps a whole extra array, while the sliding window keeps just one running sum.
In an interview, mention brute force first to show you understand the problem. Then bring up prefix sum as a speed fix. Land on the sliding window as the clean answer, since it is fast and uses almost no extra space.
Interview Insight If pushed on the space claim, point out that the sliding window keeps only a running sum and a best value. That stays fixed no matter how long the array gets, so it counts as O(1) extra space.
A few small traps catch beginners on this problem. Keep them in mind.
Run a case with all negatives and a case where k equals the array length before you call it done. These catch more bugs than any normal input will.
A: You are given an integer array and a window size k. You must find the contiguous block of k numbers that has the highest average and return that average value.
A: Every window has the same length k, so dividing by k treats all windows the same. That means the window with the largest sum also has the largest average, so we chase the sum and divide once at the end.
A: It runs in O(n) time and O(1) extra space. Each slide does one add and one subtract, no matter how large k is, so every number is touched about once.
A: Starting at 0 breaks arrays that contain all negative numbers, since no window would ever beat 0. A very low floor like negative infinity makes the first window always win the first comparison.
A: Both run in O(n) time. The prefix sum keeps a whole extra array to answer many range sums, while the sliding window keeps just one running sum and only fixes the two ends as it moves, using O(1) extra space.
Maximum Average Subarray I in Java looks tiny, yet it teaches the sliding window habit you will reuse everywhere. Once you see a fixed window as one running sum, the whole problem clicks.
Our seven-number traces showed the payoff clearly. Brute force re-added every window from scratch. The prefix sum turned each window into one subtraction, and the sliding window reused the old sum with a single add and subtract.
So take the pattern, not just the answer. When a window has a fixed size, keep a running sum and fix only the two ends as it moves. Reach for prefix sum when you need many range sums, and reach for the sliding window when the window size stays the same.
That reuse habit turns many array problems from slow into quick. It will serve you well on the harder window problems waiting further down the list.