Problem overview
The function longestSubarrayWithoutRepeating receives a slice of integers, nums, and returns the length of the longest contiguous subarray in which no value appears twice. For [2, 5, 6, 2, 3, 1, 5, 6] the answer is 5. Both [5, 6, 2, 3, 1] and [6, 2, 3, 1, 5] qualify, and no window of length 6 is free of repeats.
This lesson asks for O(n²) time and O(n) extra space. Faster solutions exist and belong to other lessons on this problem.
Approach: try every start, extend until a repeat
A subarray is fixed by its first index i and its last index j. Checking every pair (i, j) and testing each subarray for duplicates from scratch would cost O(n³). Two observations bring that down to O(n²).
- Extend one element at a time. For a fixed
i, the subarray ending atjis the previous one plusnums[j]. If it has no repeats, the only thing to check is whethernums[j]already occurs in the previous one. - Stop at the first repeat. If
nums[j]duplicates a value insidenums[i..j-1], then every longer subarray starting aticontains that same repeat. The inner loop can end right there.
To check "does this value already occur?" in O(1), keep a set of the values of the current subarray. A fresh, empty set is created for every start index.
For i = 1 in the example, the window grows as 5, 6, 2, 3, 1 (length 5), and then nums[6] is 5, which is in the set, so the inner loop stops. Starting from i = 0, the window reaches 2, 5, 6 and then stops at the second 2 with length 3.
The cost of this approach is easy to see: after a repeat is found, the work of building the window is thrown away and the next start index begins from an empty set, even though most of that window is still valid for the next start. Reusing that information is what the faster solutions do. Here the point is to see that a set turns the repeat check from a scan of the window into a single lookup, which is what takes the brute force from cubic to quadratic time.
Step by step
- Start the best length
resultat 0, which is the right answer for an empty slice. - Loop over start indices
iwith a three-partfor. - Create the set
uniqueNumswithmake(map[int]struct{})inside that loop, so every start begins with no values. - Loop over end indices
jfromi, so the one-element subarray is considered first. - Check for a repeat using comma-ok in the
ifheader. On a repeat,breakout of the inner loop. - Update the answer with the built-in
maxand the lengthj - i + 1. This line runs only when the new element was not a repeat. - Add
nums[j]to the set, after the check, so the element is not mistaken for a duplicate of itself. - Return
resultafter both loops.
The solution in Go
func longestSubarrayWithoutRepeating(nums []int) int {
result := 0
for i := 0; i < len(nums); i++ {
uniqueNums := make(map[int]struct{})
for j := i; j < len(nums); j++ {
if _, exists := uniqueNums[nums[j]]; exists {
break
}
result = max(result, j-i+1)
uniqueNums[nums[j]] = struct{}{}
}
}
return result
}
The Go details:
- The only loop is
for. The three-part forminit; condition; postis the classic counting loop, andbreakleaves the innermost loop only. - Built-in
max. Since Go 1.21,maxandminare built in and work on ordered types such asint. In older versions you would write anif. - A set as
map[int]struct{}. The empty struct carries no data.struct{}{}is a value of that type. - Comma-ok. The statement before the semicolon runs first, and
existsexists only inside theif. - Map per start index. Each outer iteration allocates a new map. The old one is garbage collected, so memory stays at
O(n).
Complexity
Time: O(n²). The outer loop runs n times and the inner loop at most n - i times, each iteration doing O(1) map work on average. For an input with all different values the total is about n² / 2 steps.
Space: O(n). The set holds at most the values of one window, never more than n.
Edge cases and common mistakes
- Empty slice. No loop runs and the result is 0.
- One element.
[4]gives 1. - All equal.
[3, 3, 3]gives 1: each window stops at its second element. - All different.
[1, 2, 3]gives 3. - Adding before checking. If
nums[j]went into the set first, every element would look like a repeat of itself and the answer would be 0. - Sharing one set across start indices. Values from the previous window would stay in the set and cut later windows short.
- Subarray versus subsequence. The elements must be contiguous. For
[1, 2, 1, 3, 4, 2]the answer is 4 ([2, 1, 3, 4]).
Testing
[2, 5, 6, 2, 3, 1, 5, 6]gives5.[]gives0.[4]gives1.[3, 3, 3]gives1.[1, 2, 3]gives3.[1, 2, 1, 3, 4, 2]gives4.