Problem overview
The function longestSubarrayWithoutRepeating(nums []int) int receives a slice of integers and returns the length of the longest subarray in which no value appears twice. A subarray is a run of consecutive elements.
For nums = [2, 5, 6, 2, 3, 1, 5, 6] the answer is 5. Both [5, 6, 2, 3, 1] and [6, 2, 3, 1, 5] have five distinct values, and no run of six elements is free of repeats.
This lesson accepts O(n^3) time and O(1) extra space, so it is the plain brute force. Faster approaches exist and come in later lessons.
Approach: try every subarray, check it for repeats
Every subarray is identified by a first index i and a last index j with i <= j. The brute force visits all of them, checks whether each is free of repeats, and keeps the longest valid length.
Checking one subarray from scratch costs O(n), and there are O(n^2) subarrays, which would give O(n^3). The solution uses two observations to avoid doing more work than needed:
- For a fixed
i,jgrows one step at a time. The subarraynums[i..j]is the previous subarraynums[i..j-1]withnums[j]added. If the previous one had no repeats, the new one has a repeat only ifnums[j]already occurs amongnums[i..j-1]. So each step needs one scan, not a full check. - Once a repeat appears, every longer subarray starting at the same
icontains that same repeat. Thejloop can stop and move on to the nexti.
The loops are still three levels deep, so the worst case is O(n^3), for example when all values are distinct and no loop stops early.
Tracing the example
Take [2, 5, 6, 2, 3, 1, 5, 6]. For i = 0, the loop extends through 2, 5 and 6, reaching length 3. At j = 3 the value 2 is already present, so the scan finds a duplicate and the j loop ends. For i = 1, the values 5, 6, 2, 3 and 1 are all new, so the length reaches 5, and at j = 6 the value 5 repeats and the loop stops. The remaining starting points reach lengths of at most 5, so result ends at 5.
Step by step
- Declare
resultwith:=and the value 0. It holds the best length so far. An empty slice has answer 0, and any real subarray replaces this value. - Loop over the first index
ifrom 0 whilei < len(nums). - Loop over the last index
jfromiwhilej < len(nums). Starting atimeans the one-element subarray is considered first. - Look for a duplicate of
nums[j]in the previous subarray,nums[i..j-1]. Go has no membership operator for slices, so the code searches by hand: setfound := false, loopkfromiwhilek < j, and whennums[k] == nums[j]setfoundtotrueandbreak. - Stop the
jloop on a duplicate. Thebreakin the previous step only leaves thekloop, so a second check,if found { break }, leaves thejloop as well. That is why the flag exists. - Update the answer. If the new element was not a duplicate,
nums[i..j]is valid and has lengthj - i + 1. When that is more thanresult, store it. - Return
resultafter the loops.
The solution in Go
func longestSubarrayWithoutRepeating(nums []int) int {
result := 0
for i := 0; i < len(nums); i++ {
for j := i; j < len(nums); j++ {
found := false
for k := i; k < j; k++ {
if nums[k] == nums[j] {
found = true
break
}
}
if found {
break
}
if j-i+1 > result {
result = j - i + 1
}
}
}
return result
}
Go details:
- Breaking out of two loops. In Go,
breakleaves only the innermostfor. A flag, as here, is one way to leave the next loop out; a labeledbreakis another. foundis declared inside thejloop, so it starts asfalsefor every newj. Declaring it outside would carry an oldtrueinto later steps.- No
elseneeded. When a duplicate is found, thebreakskips the update lines, so they run only for valid subarrays. - Half-open range. The inner loop covers
kfromiup to but not includingj, which is exactly the elements beforenums[j]. Includingjwould always findnums[j]equal to itself. - The scan by hand. Since Go 1.21 the standard library's
slices.Containsperforms the same linear search on a slice, such asnums[i:j]. Writing the loop shows what the search costs.
Complexity
Time: O(n^3). There are about n^2 / 2 pairs (i, j), and each does a scan of up to j - i elements. When all values are distinct, nothing stops early, and the total number of comparisons grows like n^3 / 6.
Space: O(1). The code keeps result, found and three loop counters.
Edge cases and common mistakes
- Empty slice. The loops never run and the result stays 0.
- Single element.
[7]gives 1. - All equal.
[4, 4, 4]gives 1: at eachi, the second element is a duplicate and ends the scan. - All distinct.
[1, 2, 3]gives 3, the whole slice. - A repeat that is not adjacent.
[1, 2, 1, 3]gives 3, from[2, 1, 3]. Only checking neighbours would miss the repeated 1. - Forgetting the second
break. Without it, the code would keep extending past a duplicate and could count a subarray that contains one. - Length arithmetic. The length of
nums[i..j]inclusive isj - i + 1, notj - i.
Testing
[2, 5, 6, 2, 3, 1, 5, 6]gives 5.[1, 2, 1, 3]gives 3.[4, 4, 4]gives 1.[1, 2, 3]gives 3.- An empty slice gives 0.