Not entirely clear one how multiple duplicates should be handled, or what you're asking precisely, but I assume it's that you want to make sure that O(1) space is satisfied, regardless of time complexity, so that's what I'll attempt to answer.
With arrays, O(1) space, O(N^2) time:
You can do it in place by simply swapping the duplicate elements to the end. You can find duplicate elements by keeping a "current" pointer and simply checking that the "next" element isn't the same as the "current". This is O(n^2) time in the worst case. Example:
[1,1,2,3,4,4,5] # "cur" is index 0 (element 1), and "next" is index 1 (element 1). Swap "next" to end.
[1,2,1,3,4,4,5] # swapping
[1,2,3,1,4,4,5] # swapping
... # Tedious swapping
[1,2,3,4,4,5,1] # Done swapping. Increment "cur".
[1,2,3,4,4,5,1] # "cur" is index 1 (element 2), and "next" is index 2 (element 3). Increment "cur"
... # Boring (no duplicates detected)
[1,2,3,4,4,5,1] # "cur" is index 3 (element 4), and "next" is index 4 (element 4). Swap "next" to end.
[1,2,3,4,5,4,1] # swapping
[1,2,3,4,5,1,4] # Done swapping. Increment "cur"
... # No more duplicates
# Done
As an aside, in practice trading time for less space typically isn't worth it. Memory is cheap, but slow response times can lose users, which is expensive. A notable exception is embedded systems where memory might be tight and inputs are short (on small inputs asymptotic runtime isn't relevant).
With linked lists, O(1) space, O(N) time:
If you had a linked list instead of an array, you could do this in O(n) time and O(1) space quite easily. Linked lists have the advantage over arrays when you're forced to "shift" elements around since they can move pointers instead of moving ALL elements by a position. The cur/next strategy is similar for linked lists as above with the array. Here's an example:
1->1->2->3->4->4->5 # "cur" is first element (value 1), and "next" is second element (value 1). Swap "next" to the end.
1
\
1->2->3->4->4->5 # Move "cur"'s pointer to "next"'s next element.
1->2->3->4->4->5->1 # Set "next"'s pointer to null, set tails pointer to "next"
... # Boring stuff with no duplicates
1->2->3->4->4->5->1 # "cur" is fourth element (value 4), and "next" is fifth element (value 4). Swap fifth element to end.
4
\
1->2->3->4->5->1 # Move "cur"'s pointer to "next"'s next element.
1->2->3->4->5->1->4 # Set "next"'s pointer to null, set tails pointer to "next"
... # No more duplicates
# Done (hopefully it's clear moving and element to the end is O(1) instead of O(n))
If you could beat an array into a linked list in O(n) time and O(1) space, the problem is solved. However, this isn't possible. Linked lists take up more space per element than an array does, so just by having a linked list anywhere in the program, I think O(1) space would be violated.
Since it was an interview question though, it might have been worth pointing out that linked lists are a bit better for solving this problem efficiently, regardless of the problem statement. Typically interviewers like to see that you can apply data structures properly, and sometimes they're amenable to an input type change.
Smart data structures and dumb code works a lot better than the other
way around. --Eric S Raymond