2
\$\begingroup\$

I ran the following code through a Leetcode.com challenge and it says the runtime for the test cases is 119ms, which puts it at the back of the pack performance wise.

The goal is to remove duplicates from a sorted array of Ints and return the count of the array without dups.

func removeDuplicates(_ nums: inout [Int]) -> Int {
 nums = Array(Set(nums)).sorted()
 return nums.count
}

I thought my solution was pretty simple, but 119ms isn't so hot compared with the average of ~60-70ms. Is there a better way to do this?

Martin R
24.2k2 gold badges38 silver badges96 bronze badges
asked Feb 23, 2017 at 14:47
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Your code works correctly, as far as I can see. A small simplification would be

func removeDuplicates(_ nums: inout [Int]) -> Int {
 nums = Set(nums).sorted()
 return nums.count
}

because sorted() can directly be applied to any collection of comparable elements and returns an array.

However, your code does not take advantage of the fact that the given array is already sorted. Two intermediate collections (a set and an array) are created, and the new array is sorted. That can be improved.

Since the array elements are sorted, you can traverse through all elements and only keep those which are different to the previous one. This leads to the following implementation:

func removeDuplicates(_ nums: inout [Int]) -> Int {
 guard let first = nums.first else {
 return 0 // Empty array
 }
 var current = first
 var uniqueNums = [current] // Keep first element
 for elem in nums.dropFirst() {
 if elem != current {
 uniqueNums.append(current) // Found a different element
 current = elem
 }
 }
 nums = uniqueNums
 return uniqueNums.count
}

In addition, you can save memory by overwriting the elements in the given array directly. If a different element is found then it is copied directly to its new position. At the end of the loop, surplus elements are removed:

func removeDuplicates(_ nums: inout [Int]) -> Int {
 guard let first = nums.first else {
 return 0 // Empty array
 }
 var current = first
 var count = 1
 for elem in nums.dropFirst() {
 if elem != current {
 nums[count] = elem
 current = elem
 count += 1
 }
 }
 nums.removeLast(nums.count - count)
 return count
}

In my tests, the last two methods were about a factor 10 faster than yours.

answered Feb 23, 2017 at 17:17
\$\endgroup\$
1
  • \$\begingroup\$ Thank you! I was so excited I could complete the challenge with two lines, only to discover it was slow, slow, slow. Your way of enumerating the array is very elegant. I've never used the dropFirst() subscript like that. \$\endgroup\$ Commented Feb 23, 2017 at 17:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.