non-repeating elements
list algorithms
space-efficient algorithms
k unique elements
programming techniques

Find the k non-repeating elements in a list with little additional space

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Finding the first k non-repeating elements sounds simple until the space constraint is added. The main tradeoff is that exact answers require remembering frequency information somewhere, so the real goal is not zero extra space, but the smallest reasonable auxiliary storage for the assumptions you have about the input.

Exact Answers Need Counting Information

If the list can contain arbitrary values and the original order must be preserved, you need a way to know whether each value appears once or more than once. For unrestricted inputs, that means storing counts in a hash table or dictionary keyed by value.

python
1def first_k_non_repeating(values, k):
2    counts = {}
3    for value in values:
4        counts[value] = counts.get(value, 0) + 1
5
6    result = []
7    for value in values:
8        if counts[value] == 1:
9            result.append(value)
10            if len(result) == k:
11                break
12
13    return result
14
15
16items = [3, 1, 2, 3, 4, 5, 1, 6, 7, 8, 9, 5]
17print(first_k_non_repeating(items, 3))

Output:

text
[2, 4, 6]

This uses O(n) time and O(u) additional space, where u is the number of distinct values. For general inputs, that is usually the correct answer.

Why “Little Additional Space” Has Limits

People often ask whether the problem can be solved with constant extra space. In the general case, not if you need exact results and stable ordering. Without storing frequency information or modifying the input in a substantial way, the algorithm has no reliable memory of which values have already been seen and how often.

So the better engineering answer is to choose the smallest structure that matches the domain:

  • dictionary for arbitrary hashable values
  • fixed-size array for a small known numeric range
  • in-place sorting only if order does not need to be preserved

When the Value Range Is Small

If the values are integers in a known bounded range, an array can be more space-efficient than a dictionary.

python
1def first_k_non_repeating_small_range(values, k, max_value):
2    counts = [0] * (max_value + 1)
3
4    for value in values:
5        counts[value] += 1
6
7    result = []
8    for value in values:
9        if counts[value] == 1:
10            result.append(value)
11            if len(result) == k:
12                break
13
14    return result
15
16
17print(first_k_non_repeating_small_range([2, 5, 2, 7, 9, 5, 11], 2, 11))

This still stores counts, but it replaces hash-table overhead with indexed storage. It only makes sense when the numeric range is small enough to justify the array.

In-Place Approaches Change the Tradeoff

If preserving original order is not required, sorting can reduce certain costs, especially in languages where in-place sort is available and you are allowed to mutate the input. After sorting, repeated values become adjacent, making it easy to identify singles.

python
1def non_repeating_after_sort(values, k):
2    values.sort()
3    result = []
4    i = 0
5
6    while i < len(values):
7        j = i + 1
8        while j < len(values) and values[j] == values[i]:
9            j += 1
10        if j == i + 1:
11            result.append(values[i])
12            if len(result) == k:
13                break
14        i = j
15
16    return result

That can reduce explicit auxiliary storage, but you lose the original sequence order. For many interview-style problems, that distinction determines whether the solution is acceptable.

Streaming Is Harder Than It Looks

If the list arrives as a stream and you want the first k non-repeating elements at the end, you still need delayed certainty. An element that has appeared once so far may repeat later. That means one-pass streaming without state is impossible for exact results. You need retained counts or a data structure that tracks both counts and arrival order.

The simplest exact solution is still the two-pass counting approach.

Common Pitfalls

  • Chasing constant extra space for arbitrary inputs when the problem fundamentally requires frequency information.
  • Forgetting that preserving input order changes which algorithms are valid.
  • Using sorting to reduce space without noticing that it changes the original sequence.
  • Stopping at the first k distinct elements instead of the first k elements that appear exactly once.
  • Choosing a fixed-size count array when the input range is too large or unknown.

Summary

  • For general inputs, the standard exact solution is a two-pass count and collect approach.
  • The space cost is O(u), which is usually the minimum practical cost for preserving order.
  • A fixed-size count array is useful only when the numeric range is known and small.
  • Sorting can reduce some overhead, but it changes the input order and therefore changes the problem.
  • The right answer depends on whether order preservation, mutability, and value range are constrained.

Course illustration
Course illustration

All Rights Reserved.