Start learning free

LESSON 284 OF 338 · SLIDING WINDOW

Longest Subarray Without Repeating in Go: O(n) Sliding Window and Set

Practice O(n) Read the full explanation

Given an input array of integers, find the length of the longest subarray without reapeating integers.

Example

Input: nums = [2, 5, 6, 2, 3, 1, 5, 6]
Output: 5
Explanation: [5, 6, 2, 3, 1] or [6, 2, 3, 1, 5] are both valid and of maximum length 5

Note:

For this lesson, your algorithm should run in O(n) time and use O(n) extra space


STEP 1 OF 9
    Ctrl + Enter to check

    GO · SLIDING WINDOW

    Longest Subarray Without Reapeating in Go, explained

    Problem overview

    The function longestSubarrayWithoutRepeating receives a slice nums of integers and returns the length of the longest contiguous subarray in which no value appears twice.

    In the example, nums = []int{2, 5, 6, 2, 3, 1, 5, 6}. The subarray [5, 6, 2, 3, 1] has five distinct values, and so does [6, 2, 3, 1, 5]. Nothing longer exists, because any window of six elements would have to contain a repeated value. The answer is 5.

    The statement asks for O(n) time and O(n) extra space.

    Approach: a window that only grows to the right

    The brute-force solution picks every start, extends the subarray until a value repeats, and records the length. Each start can scan the whole rest of the slice, so it costs O(n²).

    The waste is in the repetition. Suppose the best window starting at i ends just before index j. The elements from i + 1 to j - 1 are still distinct when the start moves to i + 1, because a subarray of a repeat-free subarray is repeat-free. So the next window doesn't need to be rebuilt: it already covers i + 1 to j - 1, and only needs to grow further from j.

    That gives the shape of the solution:

    • keep a set of the values currently in the window;
    • for each start i, push j forward while nums[j] isn't in the set, adding each value;
    • when it stops, the window holds the longest repeat-free subarray starting at i, and its size is the length of the set;
    • remove nums[i] from the set and move to i + 1, leaving j where it is.

    Since j never moves backwards, each index is added to the set once and removed once. The total work is O(n).

    The trace for the example:

    • i = 0: the window grows over indices 0 to 2 (values 2, 5, 6) and stops at index 3, because 2 is already in the set. Size 3.
    • i = 1: 2 was removed with nums[0], so the window grows over indices 1 to 5 (5, 6, 2, 3, 1) and stops at index 6, where 5 repeats. Size 5.
    • i = 2: after removing the 5, index 6 is allowed, so the window is indices 2 to 6, size 5. It stops at index 7, where 6 repeats.
    • The later starts give sizes 5, 4, 3, 2 and 1, so the best is 5.

    Step by step

    1. Create the set uniqueNums. Go has no set type, so a map[int]struct{} stands in: only the keys matter, and the empty struct struct{} as value takes no memory.
    2. Declare result as 0, the best length found so far. An empty slice gives 0.
    3. Declare j as 0, the index of the next element to add. It is declared before the loop over i so that it keeps its value when i advances.
    4. Loop over i, every possible start of the window.
    5. Grow the window with for j < len(nums). Inside it, look up nums[j] in the set. The two-value lookup _, exists := uniqueNums[nums[j]] reports presence, and writing it in the if statement's short form keeps exists local to that if. If it exists, break.
    6. Add the new value otherwise: store struct{}{} under nums[j] and increment j.
    7. Record the length. The window holds the values at indices i to j - 1, so its length is j - i, which equals len(uniqueNums). Take the larger of that and result with the built-in max.
    8. Remove nums[i] with delete before the next start. It is always in the set at this point: if the window was empty, the inner loop added at least nums[i].
    9. Return result after all starts.

    The solution in Go

    func longestSubarrayWithoutRepeating(nums []int) int {
        uniqueNums := make(map[int]struct{})
        result := 0
        j := 0
        for i := 0; i < len(nums); i++ {
            for j < len(nums) {
                if _, exists := uniqueNums[nums[j]]; exists {
                    break
                }
                uniqueNums[nums[j]] = struct{}{}
                j++
            }
            result = max(result, len(uniqueNums))
            delete(uniqueNums, nums[i])
        }
        return result
    }
    

    The Go details worth knowing:

    • The set is a map. struct{} is the type with no fields, and struct{}{} is its only value. A map[int]bool would also work, but then reading a missing key gives false, which hides the difference between "absent" and "stored as false". With struct{} you always test presence with the two-value lookup.
    • The comma-ok lookup. v, ok := m[key] returns the stored value and whether the key exists. Here the value is useless, so it is discarded with the blank identifier _. The lookup can't be written inside a for condition, which is why it sits in an if.
    • No while. for j < len(nums) is Go's while loop. The break leaves only that inner loop, not the outer one.
    • delete is built in. It works on a map and a key, and does nothing when the key is absent.
    • len on a map gives the number of keys, which is why len(uniqueNums) equals the window length.

    Complexity

    Time: O(n). The outer loop runs n times. j goes from 0 to at most n and never decreases, so across the whole run the inner loop performs at most n successful steps plus one failed check per start. Map operations take constant time on average.

    Space: O(n). In the worst case, when all values are distinct, the set holds every element.

    Edge cases and common mistakes

    • Empty slice. The loops never run and the result is 0.
    • All values equal. nums = []int{7, 7, 7} returns 1. For each start, the window can hold only the start value.
    • All values distinct. nums = []int{1, 2, 3} returns 3, taken at i = 0 when j reaches the end.
    • A repeat that is not adjacent. nums = []int{1, 2, 1, 3} returns 3, from the window [2, 1, 3]. Comparing only neighbours would miss that 1 repeats.
    • Forgetting to delete nums[i]. The set would keep values that left the window, and later windows would stop too early.
    • Rebuilding the window for every start. Resetting j to i and clearing the set gives correct answers, but the solution becomes O(n²).
    • Measuring the length as j - i + 1. When the inner loop stops, j points at the first element not in the window, so the length is j - i.
    • Confusing a subarray with a subsequence. A subarray is contiguous, so skipping elements isn't allowed.

    Testing

    • nums = []int{2, 5, 6, 2, 3, 1, 5, 6} gives 5.
    • nums = []int{} gives 0.
    • nums = []int{7, 7, 7} gives 1.
    • nums = []int{1, 2, 3} gives 3.
    • nums = []int{1, 2, 1, 3} gives 3.
    Keep going with GoAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free