Grokking the Coding Interview: Patterns for Coding Questions
Vote

0% completed

​

Solution: Find Non-Duplicate Number Instances

Problem Statement

Why this is a Two Pointers problem

Solution

Step-by-Step Algorithm

Algorithm Walkthrough

Code

Complexity Analysis

Time Complexity

Space Complexity

Similar Questions

Solution:

Complexity Analysis

Time Complexity

Space Complexity

Problem Statement

Given an array of sorted numbers, move all non-duplicate number instances at the beginning of the array in-place. The non-duplicate numbers should be sorted and you should not use any extra space so that the solution has constant space complexity i.e., O(1).

Move all the unique number instances at the beginning of the array and after moving return the length of the subarray that has no duplicate in it.

Example 1:

Input: [2, 3, 3, 3, 6, 9, 9]
Output: 4
Explanation: The first four elements after moving element will be [2, 3, 6, 9].

Example 2:

Input: [2, 2, 2, 11]
Output: 2
Explanation: The first two elements after moving elements will be [2, 11].

Constraints:

  • 1 <= nums.length <= 3 * 10<sup>4</sup>
  • -100 <= nums[i] <= 100
  • nums is sorted in non-decreasing order.

Why this is a Two Pointers problem

What the question saysThe signal it matches
"an array of sorted numbers", and the constraint "nums is sorted in non-decreasing order"the input is sorted
"in-place", and "not use any extra space so that the solution has constant space complexity"the question says in place or constant extra space
"move all non-duplicate number instances"a duplicate rule, one of the conditions this pattern handles

Nothing is being searched for from both ends, so this is not the converging shape. One pointer reads ahead while the other writes behind it, which is the same direction variant.

What almost points elsewhere. The instinct for removing duplicates is a hash set, which is the Hash Maps pattern. Two facts rule it out. The array is sorted, so duplicates already sit next to each other and there is nothing to remember. And extra space is forbidden, which a set needs.

Solution

In this problem, we need to separate the duplicate elements in-place such that the resultant length of the array remains sorted. As the input array is sorted, one way to do this is to shift the elements left whenever we encounter duplicates. In other words, we will keep one pointer for iterating the array and one pointer for placing the next non-duplicate number. So our algorithm will be to iterate the array and whenever we see a non-duplicate number we move it next to the last non-duplicate number we’ve seen.

Step-by-Step Algorithm

  • Initialize the Index: Start by initializing a variable nextNonDuplicate to 1. This variable will keep track of the position where the next unique element should be placed.
  • Iterate Through the Array: Loop through the array starting from the second element (index 1) to the end of the array.
  • Compare Elements: For each element, check if it is different from the element at the position nextNonDuplicate - 1.
    • If Different: If the current element is different from the element at nextNonDuplicate - 1, copy the current element to the position nextNonDuplicate.
    • Increment Index: Increase the nextNonDuplicate index by 1 to point to the next position for a unique element.
  • Return Result: After completing the iteration, return the value of nextNonDuplicate, which represents the number of unique elements in the array.

Algorithm Walkthrough

Let's walk through the algorithm with the array [2, 3, 3, 3, 6, 9, 9]:

  1. Initialization: Set nextNonDuplicate to 1.

    • Initial Array: [2, 3, 3, 3, 6, 9, 9]
    • nextNonDuplicate = 1
  2. Iteration:

    • i = 1:
      • Compare arr[0] (2) with arr[1] (3)
      • They are different, so copy arr[1] to arr[1] (no actual change)
      • Increment nextNonDuplicate to 2
      • Array: [2, 3, 3, 3, 6, 9, 9]
    • i = 2:
      • Compare arr[1] (3) with arr[2] (3)
      • They are the same, do nothing
      • nextNonDuplicate remains 2
    • i = 3:
      • Compare arr[1] (3) with arr[3] (3)
      • They are the same, do nothing
      • nextNonDuplicate remains 2
    • i = 4:
      • Compare arr[1] (3) with arr[4] (6)
      • They are different, so copy arr[4] to arr[2]
      • Increment nextNonDuplicate to 3
      • Array: [2, 3, 6, 3, 6, 9, 9]
    • i = 5:
      • Compare arr[2] (6) with arr[5] (9)
      • They are different, so copy arr[5] to arr[3]
      • Increment nextNonDuplicate to 4
      • Array: [2, 3, 6, 9, 6, 9, 9]
    • i = 6:
      • Compare arr[3] (9) with arr[6] (9)
      • They are the same, do nothing
      • nextNonDuplicate remains 4
  3. Result: The number of unique elements is nextNonDuplicate, which is 4. The modified array is [2, 3, 6, 9, 6, 9, 9], and the first four elements [2, 3, 6, 9] are the unique elements.

Here is the visual representation of this algorithm for Example-1:

Image

Code

Here is what our algorithm will look like:

Python3
Python3

. . . .

Complexity Analysis

Time Complexity

  • Loop through the array: The algorithm uses a single for loop to iterate through the array. Each element is processed once, so the time complexity of the loop is O(N), where N is the number of elements in the array.
  • Comparison and assignment: Inside the loop, the comparison arr[nextNonDuplicate - 1] != arr[i] and the assignment arr[nextNonDuplicate] = arr[i] are both constant time operations, O(1).

Overall time complexity: O(N).

Space Complexity

  • In-place modification: The algorithm modifies the input array in place and only uses a few extra variables (nextNonDuplicate and i), which require constant space.
  • Since no additional data structures are used that scale with the input size, the space complexity is O(1).

Overall space complexity: O(1).

Similar Questions

Problem 1: Given an unsorted array of numbers and a target ‘key’, remove all instances of ‘key’ in-place and return the new length of the array.

Example 1:

Input: [3, 2, 3, 6, 3, 10, 9, 3], Key=3
Output: 4
Explanation: The first four elements after removing every 'Key' will be [2, 6, 10, 9].

Example 2:

Input: [2, 11, 2, 2, 1], Key=2
Output: 2
Explanation: The first two elements after removing every 'Key' will be [11, 1].

Solution:

This problem is quite similar to our parent problem. We can follow a two-pointer approach and shift numbers left upon encountering the ‘key’. Here is what the code will look like:

Python3
Python3

. . . .

Complexity Analysis

Time Complexity

  • Loop through the array: The algorithm uses a single for loop to iterate through the array. Each element is processed exactly once, resulting in a time complexity of O(N), where N is the number of elements in the array.
  • Comparison and assignment: Inside the loop, the comparison arr[i] != key and the assignment arr[nextElement] = arr[i] are both constant-time operations, O(1).

Overall time complexity: O(N).

Space Complexity

  • In-place modification: The algorithm modifies the input array in place and only uses a few extra variables (nextElement and i), which require constant space.
  • Since no additional data structures are used, the space complexity is O(1).

Overall space complexity: O(1).

J

Joseph

· 4 years ago

Problem statement was a little confusing in my opinion, as the solution does not technically remove all duplicates from the array. But at the end, I suppose you could return the subarray of length nextNonDuplicate ? Image

M

monir.imamverdi

· a year ago

This page needs to be rewritten, it's so confusing.

We're expecting the first occurrence of each distinct number to be retained in the final output, rather than only keeping numbers that appear exactly once. That means instead of filtering strictly unique elements, we need to retain distinct elements in their first occurrence while shifting them to the front.

Hussain Zaidi

Hussain Zaidi

· 3 years ago

The problem says: "The relative order of the elements should be kept the same" But in the solution relative order is kept only for the non-duplicate part of the array. Which isn't what the problem suggests.

L

Luis Philipe

· 4 years ago

I believe that the "Similar Questions" requirement is quite simple, to return the new array length we just need to iterate the array and count the non "key" values. Apply two pointers on it is unnecessary.

J

John O'Neill

· 3 years ago

The problem as evaluated by the code runner doesn't actually require removing duplicates — it just requires counting non-duplicates, which isn't really a two pointers problem. To keep myself honest, I actually removed the duplicates, as below.

class Solution: def remove(self, arr): if not arr: return 0 next_non_duplicate = 1 for i, num in enumerate(arr): if arr[next_non_duplicate - 1] != num: arr[next_non_duplicate] = num next_non_duplicate += 1 n = len(arr) for _ in range(next_non_duplicate, n): arr.pop() return len(arr)
M

Mohamed Elsayed

· 5 years ago

The graphical explanation is showing that nextNoneDuplicate starts from 0 while it actually starts from 1

Show 1 reply
C

Casey Dietz

· 3 years ago

I dont understand how the elements are being removed in the solution? I just see two pointers moving through the array checking conditionals but I dont see where they are getting removed. What am I missing?

Show 1 reply
B

Brian Dy

· 4 years ago

The Similar Questions solution is missing the part where it returns a new array or subarray without the duplicates. If the current solution is acceptable, then Two Pointers is unnecessary and we could have just incremented the counter for any element != key.

Show 1 reply
Y

Yvonne

· 4 years ago

There seems to be a typo in the example visual: we start with [2, 3, 3, 3, 6, 9, 9] but end with [2, 3, 6, 6, 9, 9, 9]

Show 1 reply
T

Tomer

· 4 years ago

If the input array is empty, then the proposed solution returns 1, which is incorrect.

Show 1 reply

On This Page

Problem Statement

Why this is a Two Pointers problem

Solution

Step-by-Step Algorithm

Algorithm Walkthrough

Code

Complexity Analysis

Time Complexity

Space Complexity

Similar Questions

Solution:

Complexity Analysis

Time Complexity

Space Complexity