Problem overview
The function maxSum receives an IntArray called nums and a positive integer k. Among all contiguous subarrays of length exactly k, it must find the one with the largest sum, and return its first and last index as a two-element IntArray.
In the example, nums = [5, 6, 1, 2, 6, 6, 4, 3] and k = 3. The subarray nums[4...6] is [6, 6, 4], with sum 16, which is the largest of all windows of three. The function returns [4, 6].
Approach: add up every window
A window of length k is fixed by its first index. In an array of n elements the first index can be anything from 0 to n - k, so there are n - k + 1 windows. The brute force sums each window from scratch and remembers the best one.
Here are all six windows of the example:
| start | window | sum |
|---|---|---|
| 0 | 5, 6, 1 | 12 |
| 1 | 6, 1, 2 | 9 |
| 2 | 1, 2, 6 | 9 |
| 3 | 2, 6, 6 | 14 |
| 4 | 6, 6, 4 | 16 |
| 5 | 6, 4, 3 | 13 |
The largest sum is 16, at start 4, so the answer is [4, 4 + 3 - 1], that is [4, 6].
Each window costs k additions and there are about n windows, so the total is O(n * k). Neighbouring windows share k - 1 elements, so most of those additions repeat work already done. The next lesson on this problem slides the window instead and brings the time down to O(n).
Two questions decide the shape of the code: which windows exist, and what to remember about the best one so far. The first answer is the range of start indices, 0 to n - k. The second is a pair of values: the best sum seen, to compare against, and the indices of the window that produced it, since those are what the function must return. Nothing else has to be kept between windows.
Step by step
- Declare
maxSumwithvar, starting atInt.MIN_VALUE. It holds the best window sum so far. The smallest possibleIntguarantees that the first window replaces it, even when all the numbers are negative. - Declare
answerwithvaras an emptyIntArray, built withintArrayOf(). It will hold the start and end indices of the best window, and is replaced whenever a better one appears. - Loop
iover every window start, from 0 tonums.size - kinclusive. The last window of lengthkbegins at that index; any later start would run past the end of the array. - Declare
suminside the loop, starting at 0. Because it lives inside the loop body, every window starts counting from zero. - Loop
idxover the window's positions, fromiup to but not includingi + k. That is exactlykindices. - Add
nums[idx]tosum. - Compare after the inner loop. If
sumis greater thanmaxSum, store it inmaxSumand setanswertointArrayOf(i, i + k - 1). Using a strict>keeps the earliest window when two windows have the same sum. - Return
answerafter the outer loop.
The solution in Kotlin
class Solution {
fun maxSum(nums: IntArray, k: Int): IntArray {
var maxSum = Int.MIN_VALUE
var answer = intArrayOf()
for (i in 0..nums.size - k) {
var sum = 0
for (idx in i until i + k) {
sum += nums[idx]
}
if (sum > maxSum) {
maxSum = sum
answer = intArrayOf(i, i + k - 1)
}
}
return answer
}
}
The Kotlin details worth a closer look:
..includes both ends,untilexcludes the upper one. The outer loop needs the last startnums.size - kitself, so it uses... The inner loop needskindices fromi, soi until i + kfits.- Precedence in
0..nums.size - k. Arithmetic binds tighter than.., so this means0..(nums.size - k)and needs no parentheses. - Empty ranges. If
kis larger than the array,nums.size - kis negative, the range0..-1is empty, and the loop never runs. The function then returns the empty array. intArrayOf()and the type. With no arguments it creates anIntArrayof length 0. The variable's type isIntArray, so the later assignment ofintArrayOf(i, i + k - 1)is accepted.- Replacing instead of editing.
answeris assigned a brand-new two-element array each time a better window is found, so the array returned at the end is never shared with an earlier result. Intsums can wrap. If the window sum exceeds about 2.1 billion, it silently overflows. UseLongfor sums that large.
Complexity
Time: O(n * k). There are n - k + 1 windows and each takes k additions. When k is small this is close to linear, and when k is about half of n it is about n² / 4.
Space: O(1). Besides the two-element result, the function only uses a few integers.
Edge cases and common mistakes
kequals the array length. There is exactly one window, so the function returns[0, nums.size - 1].kis 1. The answer is the position of the largest element, and the earliest one when it repeats.- Ties. For
[2, 2, 2, 2]withk = 2every window sums to 4. The strict>keeps the first, so the result is[0, 1]. Using>=would return the last window,[2, 3]. - All negative numbers. The sentinel
Int.MIN_VALUEloses to any real sum, so the best window is still found. StartingmaxSumat 0 would never accept a negative window. - Off-by-one in the outer range. Writing
0 until nums.size - kwould skip the last window. In the example it would never test the window starting at index 5. - Returning the sum. The function returns the two indices, not the sum. The sum is only needed internally.
klarger than the array. The function returns an empty array. The lesson's inputs don't use such ak.
Testing
nums = [5, 6, 1, 2, 6, 6, 4, 3],k = 3gives[4, 6].nums = [2, 2, 2, 2],k = 2gives[0, 1].nums = [1, 2],k = 3gives[].nums = [3, 1, 2],k = 3gives[0, 2], the only window.