Quick Overview

Return each array value once in its original first-appearance order, using membership tracking without sorting the result.

Remove Duplicates While Preserving First-Appearance Order

Company: Upstart

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Online Assessment

Remove duplicate values from an array while preserving the order in which each distinct value first appears. ### Function Contract Implement `filter_duplicates(data) -> list[int]`. Return a new array containing the first occurrence of each distinct value in the input's original order. ### Constraints and Clarifications For a portable practice interface, values are integers. - `data` is always an array, never null. - `0 <= len(data) <= 200000`. - Values range from `-1000000000` through `1000000000`. - Equal values count as duplicates even when separated by other values. - Do not sort the result. - Aim for expected `O(n)` time. ### Examples ```text data = [7, 6, 4, 3, 3, 4, 9] Output: [7, 6, 4, 3, 9] ``` ```text data = [0, -1, 0, -1, 2] Output: [0, -1, 2] ``` ```hint Track prior appearances without changing traversal order Decide whether to emit a value when you first encounter it. The membership structure and the ordered result have different responsibilities. ```

Overview: Return each array value once in its original first-appearance order, using membership tracking without sorting the result.

Given an array of integers `data`, return a new array containing the first occurrence of each distinct value, in the order those first occurrences appear in `data`. Scanning `data` from left to right, a value is kept the first time it is seen and skipped on every later appearance. Two values are duplicates whenever they are equal, even when other values sit between them. Do not sort the result: the output order is exactly the order in which the kept values appear in `data`. Aim for expected `O(n)` time. The returned array is compared exactly, element by element, and the rule above fixes a single correct answer for every input, so there are no ties to break. If `data` is empty, return an empty array. The input is not modified. All values lie in `[-1000000000, 1000000000]`, so no value can exceed 2^31 - 1: `int` is sufficient in Java and C++, and every value is exactly representable as a JavaScript number. ### Examples Example 1 ```text data = [7, 6, 4, 3, 3, 4, 9] Output: [7, 6, 4, 3, 9] ``` First occurrences, in order, are 7, 6, 4 and 3; the 3 at index 4 and the 4 at index 5 repeat earlier values and are skipped; 9 is a new value and is kept. Example 2 ```text data = [0, -1, 0, -1, 2] Output: [0, -1, 2] ``` 0 and -1 each appear twice with another value in between, and equal values count as duplicates even when separated, so only their first occurrences are kept; 2 is new.

Constraints

  • `data` is always an array, never null.
  • `0 <= len(data) <= 200000`.
  • Values range from `-1000000000` through `1000000000`.
  • Equal values count as duplicates even when separated by other values.
  • Do not sort the result; it must preserve first-occurrence order.
  • Aim for expected `O(n)` time.
  • Values are integers (a portable practice interface); every value fits in a signed 32-bit integer.

Examples

Input: ([],)

Expected Output: []

Explanation: Minimum valid input: an empty array has no values to keep, so the result is empty.

Input: ([5],)

Expected Output: [5]

Explanation: Singleton: the only element is its own first occurrence.

Hints

  1. Traverse `data` once in its original order and decide about each value at the moment you first encounter it.
  2. Tracking which values have appeared before and building the ordered result are two different responsibilities; one structure does not have to do both.
  3. Re-scanning the output already built for every new element is correct but does not reach the expected O(n) time the contract asks for.

Loading coding console...

Show the approach

Approach

Algorithm: make one left-to-right pass over data, maintaining a hash set seen of the values already emitted and an output list result. For each value, if it is not in seen, insert it into seen and append it to result; otherwise skip it. Nothing is ever sorted or reordered.

Invariant: before processing index i, result holds exactly the first occurrence of every distinct value among data[0..i-1], ordered by first-occurrence index, and seen holds exactly that set of values. The step preserves it: if data[i] is in seen it occurred earlier, so index i is not a first occurrence and must be skipped; if it is not in seen it has never appeared, so index i is its first occurrence, and appending it places it after every value whose first occurrence is earlier.

Correctness: at the end of the pass (i = n) the invariant is exactly the required post-condition — each distinct value of data appears once, in first-occurrence order — so the returned array is the unique correct answer.

Edge cases: an empty input never enters the loop and returns an empty array; a singleton returns a one-element copy; an all-identical input emits only the first element; non-adjacent repeats are caught because membership is tested against the whole prefix, not just the previous element; negative values, zero and the bounds -1000000000 / 1000000000 are ordinary hash keys. All values fit in signed 32-bit, so int[] in Java and std::vector<int> in C++ suffice, and JavaScript numbers represent them exactly. The input array is read only; a new array is returned.

Complexity: one expected-O(1) hash lookup plus at most one insert and one append per element gives expected O(n) time, as the contract asks, and O(n) extra space for the set and the result (O(d) for the set, where d is the number of distinct values).

Time complexity:
O(n) expected
Space complexity:
O(n)