Problem overview
The function longestSubarray receives an IntArray called nums and an integer K. It returns the length of the longest subarray, a run of neighbouring elements, that contains at most K different values. Repeated values are fine: it is the number of distinct values that is limited, not the length.
In the example, nums = [1, 2, 1, 2, 3] and K = 2. The first four elements, [1, 2, 1, 2], contain only the values 1 and 2, so they qualify. Adding the 3 would make three distinct values, which is too many. The answer is 4.
The statement allows O(n²) time and asks for O(n) extra space.
Approach: for every start, grow the end until the limit is broken
A subarray is determined by its first and last index. The plan is to try every first index i, and for each one extend the last index j one element at a time, tracking the distinct values of nums[i..j] in a HashSet. A set ignores values it already has, so its size is exactly the number of distinct values so far.
While that size is at most K, the subarray is valid and its length, j - i + 1, is a candidate for the answer. Once the size goes above K, the loop for this i can stop. Extending a subarray never removes a value, so the number of distinct values can only stay the same or grow. Every longer subarray starting at i is therefore invalid too.
On the example with K = 2: starting at index 0, the sets are {1}, {1, 2}, {1, 2}, {1, 2}, and then {1, 2, 3} breaks the limit. The valid lengths were 1, 2, 3 and 4. Starting at index 1 reaches length 3 at best, and so on, so the overall maximum is 4.
Two nested loops over n elements make this O(n²). A sliding window can reach O(n), but that is a topic for a later lesson. The set is what keeps the quadratic version from becoming cubic, because counting distinct values by rescanning the subarray would cost another factor of n.
Step by step
- Start the answer at 0.
answeris avar, since it grows as better subarrays turn up. It holds the longest valid length found so far. - Choose the start. The outer loop,
for (i in nums.indices), tries every index as the first element. - Create a fresh set.
distinctValuesis avaldeclared inside that loop, so each start begins with an empty set. The variable is fixed, while the set's contents change. - Choose the end. The inner loop runs
joveri until nums.size. The subarray grows by one element per iteration. - Add the new element.
distinctValues.add(nums[j])updates the set. If the value was already there, the set doesn't change. - Break on too many values. If
distinctValues.size > K,breakleaves the inner loop, because no longer subarray from this start can be valid. - Record the length. Below the check, the subarray is valid, so
answerbecomesmaxOf(answer, j - i + 1). - Return
answerafter the outer loop.
The solution in Kotlin
class Solution {
fun longestSubarray(nums: IntArray, K: Int): Int {
var answer = 0
for (i in nums.indices) {
val distinctValues = HashSet<Int>()
for (j in i until nums.size) {
distinctValues.add(nums[j])
if (distinctValues.size > K) {
break
}
answer = maxOf(answer, j - i + 1)
}
}
return answer
}
}
Kotlin details:
- The parameter is named
Kin capitals. Kotlin allows that, and the name must match exactly, sincekwould be a different identifier. - The order matters: add first, then check the size. The element has to be in the set before you can know whether it pushed the count over
K. distinctValues.sizeis a property, written without parentheses. Don't confuse it withnums.size, which is the array's length.breakleaves only the inner loop. The outer loop carries on with the next start.
Complexity
Time is O(n²): there are up to n starts, each with an inner loop of up to n steps, and every step does constant-time work on average. Space is O(n), but in this solution the set never holds more than K + 1 values, because the loop breaks as soon as the size passes K. The bound can never exceed n either, since the set holds at most one entry per element.
Edge cases and common mistakes
K = 0. Adding any element makes the size 1, which is above 0, so the loop breaks before recording anything. The answer is 0. For[1, 2, 3]withK = 0the function returns 0.- Empty array. No loop runs and the answer is 0.
Kat least the number of distinct values. The whole array qualifies, and the answer isnums.size.- Checking the size after recording the length. That counts an invalid subarray as valid. The check must come first.
- Counting elements instead of distinct values. A subarray like
[1, 1, 1, 1]has one distinct value, however long it is. - Reusing the set across starts. A set declared outside the outer loop would keep values from earlier subarrays.
Testing
[1, 2, 1, 2, 3]withK = 2returns4.[1, 2, 3]withK = 0returns0.[5, 5, 5]withK = 1returns3.