Problem overview
The function longestSubarrayWithoutReapeating receives an IntArray called nums and returns the length of the longest subarray in which no integer appears twice. A subarray is a run of neighbouring elements, so you can't skip over elements.
In the example, nums = [2, 5, 6, 2, 3, 1, 5, 6]. The run [5, 6, 2, 3, 1] has five different values, and so does [6, 2, 3, 1, 5]. Nothing longer avoids a repeat, so the answer is 5.
For this lesson the statement allows O(n²) time and asks for O(n) extra space. Faster solutions exist, but this lesson builds the straightforward one first.
Approach: try every start, grow until a repeat
Every subarray is fixed by where it starts and where it ends. The idea is to try each start i and, for that start, grow the end j one element at a time, keeping the values seen so far in a HashSet. As long as the next element isn't in the set, the subarray from i to j has no repeats, and its length, j - i + 1, is a candidate for the answer.
The moment the next element is already in the set, the loop for this start can stop. Any longer subarray from the same start would still contain that repeated pair, so none of them can be valid. This early exit is what makes the set useful: without it you would have to rescan the subarray for every new element, which costs an extra factor of n.
For the example, start at index 0: the values 2, 5, 6 are new, then the second 2 is found in the set, so the best length from this start is 3. Start at index 1: 5, 6, 2, 3, 1 are all new, then the second 5 stops the loop. That gives length 5. No later start beats it.
The cost is a pair of nested loops, each running up to n times, so O(n²) time. A single sliding window can do this in linear time, but that is the idea of a later lesson.
Step by step
- Start the answer at 0.
resultis avar, because it changes whenever a longer subarray appears. It also gives the right answer, 0, for an empty array. - Choose the start. The outer loop,
for (i in nums.indices), tries every index.nums.indicesis the range0 until nums.size. - Make a fresh set per start.
uniqueNumsis created inside the outer loop, so every new start begins with no values from the previous one. It is aval: the variable stays put, and the set's contents change. - Choose the end. The inner loop,
for (j in i until nums.size), extends the subarray one element at a time.untilexcludes the upper bound, sojnever leaves the array. - Stop at a repeat. If
uniqueNumsalready containsnums[j],breakleaves the inner loop. - Record the length. Otherwise
nums[i..j]has no repeats, andmaxOf(result, j - i + 1)keeps the larger of the old best and the new length. - Remember the element. Add
nums[j]to the set so the nextjcan see it. This must come after the check; adding first would make every element a repeat of itself. - Return
resultonce every start has been tried.
The solution in Kotlin
class Solution {
fun longestSubarrayWithoutReapeating(nums: IntArray): Int {
var result = 0
for (i in nums.indices) {
val uniqueNums = HashSet<Int>()
for (j in i until nums.size) {
if (uniqueNums.contains(nums[j])) {
break
}
result = maxOf(result, j - i + 1)
uniqueNums.add(nums[j])
}
}
return result
}
}
Kotlin details:
i until nums.sizeis a half-open range: it includesiand excludesnums.size. Wheniis the last index, the range holds just that index.breakleaves only the innermost loop, which is what is wanted here: the outer loop moves on to the next start.maxOf(a, b)is a standard function that returns the larger of two values. Noiforelseis needed, since thebreakalready skips this line when there is a repeat.- Both indices are
Int. For arrays small enough to fit in memory,j - i + 1can't overflow.
Complexity
Time is O(n²): for each of the n starts, the inner loop runs at most n times, and each set operation is constant on average. Space is O(n): the set holds at most one entry per element of the subarray, and a new set replaces the old one for each start.
Edge cases and common mistakes
- Empty array. The loops don't run, and the result is 0.
- All elements equal. For
[7, 7, 7], every start breaks on its second element, so the answer is 1. - All elements different. The inner loop never breaks, and the answer is
nums.size. - Reusing one set across starts. Declaring
uniqueNumsoutside the outer loop would carry old values into new subarrays and give wrong answers. - Confusing subarray with subsequence. Elements must be adjacent. In the example, picking
2, 5, 6, 3, 1by skipping the second2isn't allowed.
Testing
[2, 5, 6, 2, 3, 1, 5, 6]returns5.[]returns0.[7, 7, 7]returns1.