Problem overview
The method longestSubarray receives an array of integers, nums, and a limit K. It must return the length of the longest subarray, a run of neighbouring elements, that contains at most K different values.
For nums = [1, 2, 1, 2, 3] and K = 2, the subarray [1, 2, 1, 2] (indices 0 to 3) holds only the values 1 and 2, so its length 4 is valid. Adding the 3 would make three distinct values, so the answer is 4.
The lesson's budget is O(n²) time and O(n) extra space.
Approach: try every start, grow until too many values
A subarray is determined by where it starts, i, and where it ends, j. The plan is to try every start. From each start, extend the end one element at a time while keeping a set of the values inside. The set's size is exactly the number of distinct values.
Once the set has more than K values, stop extending from this start. Adding more elements can keep the number of distinct values the same or raise it, never lower it, so no longer subarray from this i can be valid. Recomputing the distinct count of each subarray from scratch would cost another pass each time; maintaining the set as j advances makes each step constant time on average. Two nested loops then give O(n²).
Step by step
- Keep the best length.
answeris avarstarting at 0. - Choose each start with
for i in 0..<nums.count. - Create a fresh set.
distinctValuesis an emptySet<Int>()declared inside the outer loop, so each start begins with nothing. It is avarbecause inserting changes it. - Choose each end with
for j in i..<nums.count. The subarraynums[i...j]grows by one element per iteration. - Add
nums[j]to the set. A set ignores values it already has, so itscountis the number of distinct values innums[i...j]. - Stop when there are too many. If
distinctValues.count > K,breakout of the inner loop. - Record the length. Otherwise the subarray is valid, so update
answerwithmax(answer, j - i + 1). - Return
answerafter the outer loop.
Tracing the example
For [1, 2, 1, 2, 3] with K = 2, start i = 0 inserts 1, 2, 1 and 2 in turn. The set stays at size 2 (it holds 1 and 2), so answer climbs to 4. Inserting 3 makes the size 3, which is more than K, so the inner loop breaks. Start i = 1 sees 2, 1, 2 and then 3, giving a valid length of 3 before the break. Later starts have even fewer elements, so the final answer is 4.
The solution in Swift
class Solution {
func longestSubarray(_ nums: [Int], _ K: Int) -> Int {
var answer = 0
for i in 0..<nums.count {
var distinctValues = Set<Int>()
for j in i..<nums.count {
distinctValues.insert(nums[j])
if distinctValues.count > K {
break
}
answer = max(answer, j - i + 1)
}
}
return answer
}
}
Swift details worth a closer look:
- Insert first, then test. Here the order is the opposite of the "no repeats" version of this idea. The new value must be in the set before its size is compared with
K, because the question is how many distinct values the subarray would have. - Parameter label.
_ numsand_ Kmean the arguments are passed without labels:longestSubarray([1, 2, 1, 2, 3], 2). - Half-open ranges.
0..<nums.countstops beforenums.count. For an empty array the loop doesn't run and the result stays 0. breakexits only the inner loop.- Sets are values. A
vardeclared inside the outer loop is a brand-new set each time round.
Complexity
Time: O(n²). There are n starts and each inner loop runs at most n times, with constant average cost per set operation.
Space: O(n). The set never holds more than K + 1 values before the loop breaks, and never more than n.
Neighbouring starts repeat much of each other's work, because the set is rebuilt from nothing every time. A sliding window that keeps one set of counts and moves its left edge only when needed avoids that and reaches O(n). This lesson uses the simpler nested loops.
Edge cases and common mistakes
- Empty array. Returns 0, because the outer loop has no starts to try.
- Distinct is not the same as length. The limit counts different values, not elements, so
[1, 1, 1, 1, 2]withK = 2is valid as a whole and returns 5. K = 0. The first inserted value already makes the count 1, which is more than 0, so every start breaks at once and the result is 0. For[1, 2, 3]withK = 0the answer is 0.Kat least the number of distinct values. The whole array is valid.[1, 2, 3, 4]withK = 10returns 4.- All values equal.
[4, 4, 4]withK = 1returns 3. - Updating
answerbefore the check. If themaxline ran before thebreaktest, the invalid subarray's length would be counted. - Sharing one set across starts. Declare the set inside the outer loop, otherwise values from earlier starts inflate the count.
Testing
[1, 2, 1, 2, 3]withK = 2gives 4.[1, 2, 3]withK = 0gives 0.[4, 4, 4]withK = 1gives 3.[1, 2, 3, 4]withK = 10gives 4.[]withK = 3gives 0.
