Previous 65 byte solution:
r->{for(int a,b=0,z,i=0;;b=a)if((a=b|1<<(z=r[i++]))==b)return z;}
New solution. 19 bytes are included for import java.math.*;
-8 bytes thanks to @Nevay
r->{int z,i=0;for(BigInteger c=BigInteger.ZERO;c.min(c=c.setBit(z=r[i++]))!=c;);return z;}
Try it online!
Edit
The algorithm in my original program was fine, but the static size of the datatype used meant that it broke fairly quickly once the size went above a certain threshold.
I have changed the datatype used in the calculation to increase the memory limit of the program to accommodate this (using BigInteger
for arbitrary precision instead of int
or long
). However, this makes it debatable whether or not this counts as O(1)
space complexity.
I will leave my explanation below intact, but I wish to add that I now believe it is impossible to achieve O(1)
space complexity without making some assumptions.
Proof
Define N
as an integer such that 2 <= N
.
Let S
be a list representing a series of random integers [x{1}, ..., x{N}]
, where x{i}
has the constraint 1 <= x{i} <= N
.
The time complexity (in Big-O notation) required to iterate through this list exactly once per element is O(n)
The challenge given is to find the first duplicated value in the list. More specifically, we are searching for the first value in S
that is a duplicate of a previous item on the list.
Let p
and q
be the positions of two elements in the list such that p < q
and x{p} == x{q}
. Our challenge becomes finding the smallest q
that satisfies those conditions.
The obvious approach to this problem is to iterate through S and check if our x{i}
exists in another list T
:
If x{i}
does not exist in T
, we store it in T
.
If x{i}
does exist in T
, it is the first duplicate value and therefore the smallest q
, and as such we return it.
This space efficiency is O(n)
.
In order to achieve O(1)
space complexity while maintaining O(n)
time complexity, we have to store unique information about each object in the list in a finite amount of space. Because of this, the only way any algorithm could perform at O(1)
space complexity is if:
1. N is given an upper bound corresponding to the memory required to store the maximum number of possible values for a particular finite datatype.
2. The re-assignment of a single immutable variable is not counted against the complexity, only the number of variables (a list being multiple variables).
3. (Based on other answers) The list is (or at least, the elements of the list are) mutable, and the datatype of the list is preset as a signed integer, allowing for changes to be made to elements further in the list without using additional memory.
1 and 3 both require assumptions and specifications about the datatype, while 2 requires that only the number of variables be considered for the calculation of space complexity, rather than the size of those variables. If none of these assumptions are accepted, it would be impossible to achieve both O(n)
time complexity and O(1)
space complexity.
Explanation
Whoo boy, this one took an embarrassingly long time to think up a bit of brain power.
So, going for the bonus is difficult. We need both to operate over the entire list exactly once and track which values we've already iterated over without additional space complexity.
Bit manipulation solves those problems. We initialize our O(1)
'storage', a pair of integers, then iterate through the list, OR-ing the ith bit in our first integer and storing that result to the second.
For instance, if we have 1101
, and we perform an OR operation with 10
, we get 1111
. If we do another OR with 10
, we still have 1101
.
Ergo, once we perform the OR operation and end up with the same number, we've found our duplicate. No duplicates in the array causes the program to run over and throw an exception.